@wenrwa/marketplace-cli 0.1.0 → 0.2.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 (52) hide show
  1. package/README.md +14 -0
  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 +174 -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 +8 -0
  34. package/dist/commands/dashboard.d.ts +31 -0
  35. package/dist/commands/dashboard.js +5 -0
  36. package/dist/commands/treasury.d.ts +0 -0
  37. package/dist/commands/treasury.js +2 -2
  38. package/dist/commands/webhook.d.ts +2 -0
  39. package/dist/commands/webhook.js +131 -0
  40. package/dist/commands/workspace.d.ts +0 -0
  41. package/dist/commands/workspace.js +160 -13
  42. package/dist/index.d.ts +0 -0
  43. package/dist/index.js +4 -0
  44. package/dist/utils/client.d.ts +1 -0
  45. package/dist/utils/client.js +3 -0
  46. package/dist/utils/config.d.ts +0 -0
  47. package/dist/utils/config.js +0 -0
  48. package/dist/utils/format.d.ts +0 -1
  49. package/dist/utils/format.js +2 -11
  50. package/dist/utils/websocket.d.ts +0 -0
  51. package/dist/utils/websocket.js +0 -0
  52. package/package.json +5 -2
@@ -0,0 +1,61 @@
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 websocket_1 = require("../utils/websocket");
8
+ beforeAll(() => {
9
+ chalk_1.default.level = 0;
10
+ });
11
+ function makeEvent(event, data, timestamp) {
12
+ return { event, data, timestamp: timestamp || '2024-06-15T14:30:00.000Z' };
13
+ }
14
+ describe('formatEventPretty()', () => {
15
+ it('formats bounty:created', () => {
16
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('bounty:created', { title: 'Build API', rewardAmount: '100', rewardSymbol: 'USDC' }));
17
+ expect(result).toContain('[new]');
18
+ expect(result).toContain('Build API');
19
+ });
20
+ it('formats bounty:bid', () => {
21
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('bounty:bid', { agentWallet: 'abcdefghijklmnop', bidAmount: '50', bountyId: 'bounty123456789' }));
22
+ expect(result).toContain('[bid]');
23
+ });
24
+ it('formats bounty:assigned', () => {
25
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('bounty:assigned', { bountyId: 'bounty123456789', assigneeWallet: 'wallet12345678' }));
26
+ expect(result).toContain('[assigned]');
27
+ });
28
+ it('formats bounty:progress', () => {
29
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('bounty:progress', { percentage: 50, bountyId: 'bounty123456789', message: 'Half done' }));
30
+ expect(result).toContain('[progress]');
31
+ expect(result).toContain('50%');
32
+ });
33
+ it('formats bounty:completed', () => {
34
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('bounty:completed', { bountyId: 'bounty123456789' }));
35
+ expect(result).toContain('[complete]');
36
+ });
37
+ it('formats unknown event as fallback', () => {
38
+ const result = (0, websocket_1.formatEventPretty)(makeEvent('custom:event', { key: 'value' }));
39
+ expect(result).toContain('[custom:event]');
40
+ });
41
+ it('uses current time when timestamp is missing', () => {
42
+ const event = { event: 'bounty:completed', data: { bountyId: 'b1' } };
43
+ const result = (0, websocket_1.formatEventPretty)(event);
44
+ expect(result).toContain('[complete]');
45
+ });
46
+ });
47
+ describe('formatEventJson()', () => {
48
+ it('returns JSON with ts, event, and data', () => {
49
+ const event = makeEvent('bounty:created', { title: 'Test' });
50
+ const result = JSON.parse((0, websocket_1.formatEventJson)(event));
51
+ expect(result.ts).toBe('2024-06-15T14:30:00.000Z');
52
+ expect(result.event).toBe('bounty:created');
53
+ expect(result.data).toEqual({ title: 'Test' });
54
+ });
55
+ it('uses current time when timestamp missing', () => {
56
+ const event = { event: 'test', data: {} };
57
+ const result = JSON.parse((0, websocket_1.formatEventJson)(event));
58
+ expect(result.ts).toBeDefined();
59
+ expect(result.event).toBe('test');
60
+ });
61
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,105 @@
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
+ formatEventJson: jest.fn(),
17
+ }));
18
+ const workspace_1 = require("../commands/workspace");
19
+ beforeAll(() => {
20
+ chalk_1.default.level = 0;
21
+ });
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
25
+ async function runCommand(...args) {
26
+ await workspace_1.workspaceCommand.parseAsync(args, { from: 'user' });
27
+ }
28
+ describe('workspace list', () => {
29
+ it('renders a table of workspaces', async () => {
30
+ mockClient.get.mockResolvedValue({
31
+ workspaces: [
32
+ { id: 'ws-abc-12345678', name: 'Test Workspace', mode: 'open', bounty_count: 5, agent_count: 2, created_at: '2024-01-01' },
33
+ ],
34
+ });
35
+ const spy = jest.spyOn(console, 'log').mockImplementation();
36
+ await runCommand('list');
37
+ expect(mockClient.get).toHaveBeenCalledWith('/workspaces');
38
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
39
+ expect(output).toContain('Test Workspace');
40
+ spy.mockRestore();
41
+ });
42
+ it('handles empty workspace list', async () => {
43
+ mockClient.get.mockResolvedValue({ workspaces: [] });
44
+ const spy = jest.spyOn(console, 'log').mockImplementation();
45
+ await runCommand('list');
46
+ expect(spy).toHaveBeenCalledWith('No workspaces found.');
47
+ spy.mockRestore();
48
+ });
49
+ it('handles API error', async () => {
50
+ mockClient.get.mockRejectedValue(new Error('Network error'));
51
+ const errSpy = jest.spyOn(console, 'error').mockImplementation();
52
+ await runCommand('list');
53
+ expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('Network error'));
54
+ errSpy.mockRestore();
55
+ });
56
+ });
57
+ describe('workspace status', () => {
58
+ it('displays workspace details with bounties and treasury', async () => {
59
+ mockClient.get.mockImplementation((path) => {
60
+ if (path.includes('/treasury')) {
61
+ return Promise.resolve({ balance: '1000', total_funded: '5000', total_paid: '4000' });
62
+ }
63
+ if (path.includes('/bounties')) {
64
+ return Promise.resolve({
65
+ bounties: [{ title: 'Build Feature', status: 'open', assignee_wallet: null, reward_amount: '100', reward_symbol: 'USDC', blocked_by: [] }],
66
+ });
67
+ }
68
+ return Promise.resolve({
69
+ workspace: { id: 'ws1', name: 'Main', mode: 'open', use_escrow: true, created_at: '2024-01-01' },
70
+ });
71
+ });
72
+ const spy = jest.spyOn(console, 'log').mockImplementation();
73
+ await runCommand('status', 'ws1');
74
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
75
+ expect(output).toContain('Main');
76
+ expect(output).toContain('Build Feature');
77
+ expect(output).toContain('Treasury');
78
+ spy.mockRestore();
79
+ });
80
+ it('handles missing treasury gracefully', async () => {
81
+ mockClient.get.mockImplementation((path) => {
82
+ if (path.includes('/treasury'))
83
+ return Promise.reject(new Error('Not found'));
84
+ if (path.includes('/bounties'))
85
+ return Promise.resolve({ bounties: [] });
86
+ return Promise.resolve({
87
+ workspace: { id: 'ws1', name: 'Test WS', mode: 'open', use_escrow: false, created_at: '2024-01-01' },
88
+ });
89
+ });
90
+ const spy = jest.spyOn(console, 'log').mockImplementation();
91
+ await runCommand('status', 'ws1');
92
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
93
+ expect(output).toContain('Test WS');
94
+ // Treasury heading should NOT appear since the endpoint rejects
95
+ expect(output).not.toContain('Balance:');
96
+ spy.mockRestore();
97
+ });
98
+ it('handles workspace fetch error', async () => {
99
+ mockClient.get.mockRejectedValue(new Error('Workspace not found'));
100
+ const errSpy = jest.spyOn(console, 'error').mockImplementation();
101
+ await runCommand('status', 'ws-bad');
102
+ expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('Workspace not found'));
103
+ errSpy.mockRestore();
104
+ });
105
+ });
File without changes
File without changes
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const appsCommand: Command;
@@ -0,0 +1,174 @@
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.appsCommand = 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 fs_1 = require("fs");
12
+ exports.appsCommand = new commander_1.Command('apps').description('Hosted apps management');
13
+ exports.appsCommand
14
+ .command('list')
15
+ .description('List your hosted apps')
16
+ .option('--status <status>', 'Filter by status (pending, building, deployed, failed)')
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();
22
+ params.set('ownerWallet', client.getApiKey());
23
+ if (opts.status)
24
+ params.set('status', opts.status);
25
+ params.set('limit', opts.limit);
26
+ const data = await client.get(`/projects?${params}`);
27
+ const projects = data.projects || [];
28
+ if (projects.length === 0) {
29
+ console.log('No apps found. Create one with: wen-market apps create');
30
+ return;
31
+ }
32
+ console.log(`${chalk_1.default.dim(`Showing ${projects.length} of ${data.total || projects.length} apps`)}\n`);
33
+ console.log((0, format_1.makeTable)(['ID', 'Title', 'Slug', 'Status', 'URL', 'Created'], projects.map((p) => [
34
+ (0, format_1.shorten)(p.id),
35
+ p.title.slice(0, 30),
36
+ `${p.slug}.wenrwa.app`,
37
+ (0, format_1.statusColor)(p.status),
38
+ p.deployed_url || p.preview_url || '—',
39
+ p.created_at ? (0, format_1.formatTimeAgo)(p.created_at) : '—',
40
+ ])));
41
+ }
42
+ catch (err) {
43
+ (0, format_1.error)(err.message);
44
+ }
45
+ });
46
+ exports.appsCommand
47
+ .command('create')
48
+ .description('Create a new hosted app')
49
+ .requiredOption('--title <title>', 'App title')
50
+ .requiredOption('--reward <amount>', 'Reward amount in USDC')
51
+ .requiredOption('--deadline <date>', 'Deadline (ISO 8601)')
52
+ .option('--description <desc>', 'App description')
53
+ .option('--framework <fw>', 'Framework (react, nextjs, vue, svelte, vanilla)', 'react')
54
+ .option('--visibility <vis>', 'Visibility (public, unlisted)', 'public')
55
+ .action(async (opts) => {
56
+ try {
57
+ const client = (0, client_1.getClient)();
58
+ const amountMicro = String(Math.round(parseFloat(opts.reward) * 1e6));
59
+ const data = await client.post('/projects', {
60
+ title: opts.title,
61
+ description: opts.description || opts.title,
62
+ framework: opts.framework,
63
+ visibility: opts.visibility,
64
+ rewardAmount: amountMicro,
65
+ deadline: new Date(opts.deadline).toISOString(),
66
+ });
67
+ (0, format_1.heading)('App Created');
68
+ console.log(` Project ID: ${data.project.id}`);
69
+ console.log(` Bounty ID: ${data.bountyId}`);
70
+ console.log(` Slug: ${data.project.slug}.wenrwa.app`);
71
+ console.log(` Status: ${(0, format_1.statusColor)(data.project.status)}`);
72
+ }
73
+ catch (err) {
74
+ (0, format_1.error)(err.message);
75
+ }
76
+ });
77
+ exports.appsCommand
78
+ .command('status')
79
+ .argument('<id>', 'Project ID')
80
+ .description('Show app details')
81
+ .action(async (id) => {
82
+ try {
83
+ const client = (0, client_1.getClient)();
84
+ const data = await client.get(`/projects/${id}`);
85
+ const p = data.project;
86
+ (0, format_1.heading)(`App: ${p.title}`);
87
+ console.log(` ID: ${p.id}`);
88
+ console.log(` Slug: ${p.slug}.wenrwa.app`);
89
+ console.log(` Status: ${(0, format_1.statusColor)(p.status)}`);
90
+ console.log(` Framework: ${p.framework || '—'}`);
91
+ console.log(` Visibility: ${p.visibility}`);
92
+ console.log(` Preview URL: ${p.preview_url || '—'}`);
93
+ console.log(` Deployed URL: ${p.deployed_url || '—'}`);
94
+ console.log(` Created: ${p.created_at}`);
95
+ if (p.deployed_at)
96
+ console.log(` Deployed: ${p.deployed_at}`);
97
+ }
98
+ catch (err) {
99
+ (0, format_1.error)(err.message);
100
+ }
101
+ });
102
+ exports.appsCommand
103
+ .command('download')
104
+ .argument('<id>', 'Project ID')
105
+ .option('-o, --output <path>', 'Output file path', 'source.zip')
106
+ .description('Download source.zip')
107
+ .action(async (id, opts) => {
108
+ try {
109
+ const client = (0, client_1.getClient)();
110
+ const url = `${client.getApiUrl()}/projects/${id}/source`;
111
+ const res = await fetch(url, {
112
+ headers: { 'X-Wallet-Pubkey': client.getApiKey() },
113
+ });
114
+ if (!res.ok) {
115
+ const data = await res.json().catch(() => ({}));
116
+ throw new Error(data?.error?.message || `HTTP ${res.status}`);
117
+ }
118
+ const buffer = Buffer.from(await res.arrayBuffer());
119
+ (0, fs_1.writeFileSync)(opts.output, buffer);
120
+ console.log(chalk_1.default.green(`Downloaded to ${opts.output} (${buffer.length} bytes)`));
121
+ }
122
+ catch (err) {
123
+ (0, format_1.error)(err.message);
124
+ }
125
+ });
126
+ exports.appsCommand
127
+ .command('export-github')
128
+ .argument('<id>', 'Project ID')
129
+ .option('--repo <name>', 'GitHub repo name')
130
+ .option('--private', 'Create private repo')
131
+ .description('Export source to GitHub')
132
+ .action(async (id, opts) => {
133
+ try {
134
+ const client = (0, client_1.getClient)();
135
+ const data = await client.post(`/projects/${id}/export-github`, {
136
+ repoName: opts.repo,
137
+ isPrivate: opts.private ?? false,
138
+ });
139
+ console.log(chalk_1.default.green(`Exported to GitHub: ${data.repoUrl}`));
140
+ }
141
+ catch (err) {
142
+ if (err.message?.includes('not connected')) {
143
+ console.log(chalk_1.default.yellow('GitHub not connected. Visit the web app to complete OAuth.'));
144
+ }
145
+ else {
146
+ (0, format_1.error)(err.message);
147
+ }
148
+ }
149
+ });
150
+ exports.appsCommand
151
+ .command('deployments')
152
+ .argument('<id>', 'Project ID')
153
+ .description('List deployment history')
154
+ .action(async (id) => {
155
+ try {
156
+ const client = (0, client_1.getClient)();
157
+ const data = await client.get(`/projects/${id}/deployments`);
158
+ const deployments = data.deployments || [];
159
+ if (deployments.length === 0) {
160
+ console.log('No deployments yet.');
161
+ return;
162
+ }
163
+ console.log((0, format_1.makeTable)(['ID', 'Status', 'URL', 'Initiated By', 'Created'], deployments.map((d) => [
164
+ (0, format_1.shorten)(d.id),
165
+ (0, format_1.statusColor)(d.status),
166
+ d.deployed_url || '—',
167
+ (0, format_1.shorten)(d.initiated_by),
168
+ d.created_at ? (0, format_1.formatTimeAgo)(d.created_at) : '—',
169
+ ])));
170
+ }
171
+ catch (err) {
172
+ (0, format_1.error)(err.message);
173
+ }
174
+ });
File without changes
File without changes
File without changes
@@ -65,6 +65,10 @@ exports.bountyCommand
65
65
  console.log(` Assignee: ${b.assignee_wallet || '—'}`);
66
66
  console.log(` Assignment: ${b.assignment_mode}`);
67
67
  console.log(` Verification: ${b.verification_mode}`);
68
+ if (b.expected_deliverable_type && b.expected_deliverable_type !== 'generic') {
69
+ const labels = { pr: 'Pull Request', document: 'Document', report: 'Report' };
70
+ console.log(` Deliverable: ${labels[b.expected_deliverable_type] || b.expected_deliverable_type}`);
71
+ }
68
72
  console.log(` Deadline: ${b.deadline || '—'}`);
69
73
  console.log(` Max retries: ${b.max_retries ?? 0}`);
70
74
  console.log(` Retry count: ${b.retry_count ?? 0}`);
@@ -75,6 +79,10 @@ exports.bountyCommand
75
79
  if (b.result_url) {
76
80
  console.log(` Result URL: ${b.result_url}`);
77
81
  }
82
+ // Show PR URL from latest submission if available
83
+ if (b.pr_url) {
84
+ console.log(` PR URL: ${b.pr_url}`);
85
+ }
78
86
  if (b.task_schema) {
79
87
  (0, format_1.heading)('Task Schema');
80
88
  console.log(JSON.stringify(b.task_schema, null, 2));
@@ -1,2 +1,33 @@
1
1
  import { Command } from 'commander';
2
+ import { WsEvent } from '../utils/websocket';
3
+ export interface BountyRow {
4
+ id: string;
5
+ title: string;
6
+ status: string;
7
+ assignee_wallet?: string;
8
+ percentage?: number;
9
+ last_heartbeat?: string;
10
+ reward_amount: string;
11
+ reward_symbol: string;
12
+ }
13
+ export interface DashboardState {
14
+ workspace: {
15
+ id: string;
16
+ name: string;
17
+ mode: string;
18
+ };
19
+ bounties: BountyRow[];
20
+ treasury: {
21
+ balance: string;
22
+ total_funded: string;
23
+ total_paid: string;
24
+ };
25
+ events: string[];
26
+ startTime: number;
27
+ }
2
28
  export declare const dashboardCommand: Command;
29
+ export declare function updateBountyFromEvent(state: DashboardState, event: WsEvent): void;
30
+ export declare function pad(s: string, len: number, right?: boolean): string;
31
+ export declare function colorOverhead(status: string): number;
32
+ export declare function heartbeatColor(hb: string): string;
33
+ export declare function formatUptime(ms: number): string;
@@ -4,6 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.dashboardCommand = void 0;
7
+ exports.updateBountyFromEvent = updateBountyFromEvent;
8
+ exports.pad = pad;
9
+ exports.colorOverhead = colorOverhead;
10
+ exports.heartbeatColor = heartbeatColor;
11
+ exports.formatUptime = formatUptime;
7
12
  const commander_1 = require("commander");
8
13
  const chalk_1 = __importDefault(require("chalk"));
9
14
  const client_1 = require("../utils/client");
File without changes
@@ -57,7 +57,7 @@ exports.treasuryCommand
57
57
  exports.treasuryCommand
58
58
  .command('fund')
59
59
  .argument('<workspace-id>', 'Workspace ID')
60
- .requiredOption('--amount <amount>', 'Amount in lamports to fund')
60
+ .requiredOption('--amount <amount>', 'Amount in USDC micro-units')
61
61
  .option('--tx-signature <sig>', 'On-chain transaction signature')
62
62
  .description('Fund workspace treasury')
63
63
  .action(async (workspaceId, opts) => {
@@ -76,7 +76,7 @@ exports.treasuryCommand
76
76
  exports.treasuryCommand
77
77
  .command('distribute')
78
78
  .argument('<workspace-id>', 'Workspace ID')
79
- .requiredOption('--per-agent <amount>', 'Amount per agent in lamports')
79
+ .requiredOption('--per-agent <amount>', 'Amount per agent in USDC micro-units')
80
80
  .description('Distribute funds to workspace agents')
81
81
  .action(async (workspaceId, opts) => {
82
82
  try {
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const webhookCommand: Command;
@@ -0,0 +1,131 @@
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.webhookCommand = 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.webhookCommand = new commander_1.Command('webhook').description('Webhook subscription management');
12
+ exports.webhookCommand
13
+ .command('create')
14
+ .requiredOption('--url <url>', 'Webhook callback URL (HTTPS)')
15
+ .requiredOption('--events <types...>', 'Event types to subscribe to (e.g. bounty:created bounty:completed)')
16
+ .option('--workspace <id>', 'Filter to a specific workspace')
17
+ .option('--bounty <id>', 'Filter to a specific bounty')
18
+ .description('Create a new webhook subscription')
19
+ .action(async (opts) => {
20
+ try {
21
+ const client = (0, client_1.getClient)();
22
+ const data = await client.post('/webhooks', {
23
+ url: opts.url,
24
+ eventTypes: opts.events,
25
+ filterWorkspaceId: opts.workspace,
26
+ filterBountyId: opts.bounty,
27
+ });
28
+ const sub = data.subscription;
29
+ (0, format_1.success)('Webhook created');
30
+ console.log(` ID: ${sub.id}`);
31
+ console.log(` URL: ${sub.url}`);
32
+ console.log(` Events: ${sub.eventTypes.join(', ')}`);
33
+ console.log();
34
+ console.log(chalk_1.default.yellow.bold(' Secret (shown once — save it now):'));
35
+ console.log(chalk_1.default.yellow(` ${sub.secret}`));
36
+ }
37
+ catch (err) {
38
+ (0, format_1.error)(err.message);
39
+ }
40
+ });
41
+ exports.webhookCommand
42
+ .command('list')
43
+ .description('List your webhook subscriptions')
44
+ .action(async () => {
45
+ try {
46
+ const client = (0, client_1.getClient)();
47
+ const data = await client.get('/webhooks');
48
+ const subs = data.subscriptions || [];
49
+ if (subs.length === 0) {
50
+ console.log(chalk_1.default.dim(' No webhook subscriptions'));
51
+ return;
52
+ }
53
+ (0, format_1.heading)('Webhook Subscriptions');
54
+ console.log((0, format_1.makeTable)(['ID', 'URL', 'Events', 'Active', 'Failures', 'Last Delivery'], subs.map((s) => [
55
+ (0, format_1.shorten)(s.id),
56
+ s.url.length > 40 ? s.url.slice(0, 37) + '...' : s.url,
57
+ s.eventTypes.length > 2
58
+ ? `${s.eventTypes.slice(0, 2).join(', ')} +${s.eventTypes.length - 2}`
59
+ : s.eventTypes.join(', '),
60
+ s.isActive ? chalk_1.default.green('yes') : chalk_1.default.red('no'),
61
+ s.consecutiveFailures > 0 ? chalk_1.default.red(String(s.consecutiveFailures)) : '0',
62
+ s.lastDeliveryAt ? (0, format_1.formatTimeAgo)(s.lastDeliveryAt) : chalk_1.default.dim('never'),
63
+ ])));
64
+ }
65
+ catch (err) {
66
+ (0, format_1.error)(err.message);
67
+ }
68
+ });
69
+ exports.webhookCommand
70
+ .command('delete')
71
+ .argument('<id>', 'Webhook subscription ID')
72
+ .description('Delete a webhook subscription')
73
+ .action(async (id) => {
74
+ try {
75
+ const client = (0, client_1.getClient)();
76
+ await client.del(`/webhooks/${id}`);
77
+ (0, format_1.success)(`Webhook ${(0, format_1.shorten)(id)} deleted`);
78
+ }
79
+ catch (err) {
80
+ (0, format_1.error)(err.message);
81
+ }
82
+ });
83
+ exports.webhookCommand
84
+ .command('deliveries')
85
+ .argument('<id>', 'Webhook subscription ID')
86
+ .option('--limit <n>', 'Number of deliveries to show', '10')
87
+ .description('Show recent delivery attempts for a webhook')
88
+ .action(async (id, opts) => {
89
+ try {
90
+ const client = (0, client_1.getClient)();
91
+ const data = await client.get(`/webhooks/${id}/deliveries?limit=${opts.limit}`);
92
+ const deliveries = data.deliveries || [];
93
+ if (deliveries.length === 0) {
94
+ console.log(chalk_1.default.dim(' No deliveries yet'));
95
+ return;
96
+ }
97
+ (0, format_1.heading)('Webhook Deliveries');
98
+ console.log((0, format_1.makeTable)(['Event', 'Type', 'Attempt', 'Status', 'HTTP', 'Time'], deliveries.map((d) => [
99
+ (0, format_1.shorten)(d.eventId),
100
+ d.eventType,
101
+ String(d.attempt),
102
+ (0, format_1.statusColor)(d.status),
103
+ d.httpStatus ? String(d.httpStatus) : chalk_1.default.dim('—'),
104
+ d.createdAt ? (0, format_1.formatTimeAgo)(d.createdAt) : '',
105
+ ])));
106
+ }
107
+ catch (err) {
108
+ (0, format_1.error)(err.message);
109
+ }
110
+ });
111
+ exports.webhookCommand
112
+ .command('test')
113
+ .argument('<id>', 'Webhook subscription ID')
114
+ .description('Send a synthetic test event to a webhook')
115
+ .action(async (id) => {
116
+ try {
117
+ const client = (0, client_1.getClient)();
118
+ console.log(`Sending test event to webhook ${(0, format_1.shorten)(id)}...`);
119
+ const data = await client.post(`/webhooks/${id}/test`, {});
120
+ const delivery = data.delivery;
121
+ if (delivery.status === 'success') {
122
+ (0, format_1.success)(`Test delivered successfully (HTTP ${delivery.httpStatus})`);
123
+ }
124
+ else {
125
+ (0, format_1.error)(`Test delivery failed: ${delivery.errorMessage || `HTTP ${delivery.httpStatus}`}`);
126
+ }
127
+ }
128
+ catch (err) {
129
+ (0, format_1.error)(err.message);
130
+ }
131
+ });
File without changes