@proxygate/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.
Files changed (249) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +311 -0
  3. package/dist/commands/apis.d.ts +3 -0
  4. package/dist/commands/apis.d.ts.map +1 -0
  5. package/dist/commands/apis.js +68 -0
  6. package/dist/commands/apis.js.map +1 -0
  7. package/dist/commands/apis.test.d.ts +2 -0
  8. package/dist/commands/apis.test.d.ts.map +1 -0
  9. package/dist/commands/apis.test.js +103 -0
  10. package/dist/commands/apis.test.js.map +1 -0
  11. package/dist/commands/balance.d.ts +9 -0
  12. package/dist/commands/balance.d.ts.map +1 -0
  13. package/dist/commands/balance.js +50 -0
  14. package/dist/commands/balance.js.map +1 -0
  15. package/dist/commands/balance.test.d.ts +2 -0
  16. package/dist/commands/balance.test.d.ts.map +1 -0
  17. package/dist/commands/balance.test.js +106 -0
  18. package/dist/commands/balance.test.js.map +1 -0
  19. package/dist/commands/categories.d.ts +3 -0
  20. package/dist/commands/categories.d.ts.map +1 -0
  21. package/dist/commands/categories.js +45 -0
  22. package/dist/commands/categories.js.map +1 -0
  23. package/dist/commands/categories.test.d.ts +2 -0
  24. package/dist/commands/categories.test.d.ts.map +1 -0
  25. package/dist/commands/categories.test.js +84 -0
  26. package/dist/commands/categories.test.js.map +1 -0
  27. package/dist/commands/create.d.ts +3 -0
  28. package/dist/commands/create.d.ts.map +1 -0
  29. package/dist/commands/create.js +136 -0
  30. package/dist/commands/create.js.map +1 -0
  31. package/dist/commands/create.test.d.ts +2 -0
  32. package/dist/commands/create.test.d.ts.map +1 -0
  33. package/dist/commands/create.test.js +95 -0
  34. package/dist/commands/create.test.js.map +1 -0
  35. package/dist/commands/deposit.d.ts +9 -0
  36. package/dist/commands/deposit.d.ts.map +1 -0
  37. package/dist/commands/deposit.js +78 -0
  38. package/dist/commands/deposit.js.map +1 -0
  39. package/dist/commands/deposit.test.d.ts +2 -0
  40. package/dist/commands/deposit.test.d.ts.map +1 -0
  41. package/dist/commands/deposit.test.js +93 -0
  42. package/dist/commands/deposit.test.js.map +1 -0
  43. package/dist/commands/dev.d.ts +3 -0
  44. package/dist/commands/dev.d.ts.map +1 -0
  45. package/dist/commands/dev.js +188 -0
  46. package/dist/commands/dev.js.map +1 -0
  47. package/dist/commands/dev.test.d.ts +2 -0
  48. package/dist/commands/dev.test.d.ts.map +1 -0
  49. package/dist/commands/dev.test.js +89 -0
  50. package/dist/commands/dev.test.js.map +1 -0
  51. package/dist/commands/getting-started-steps.d.ts +12 -0
  52. package/dist/commands/getting-started-steps.d.ts.map +1 -0
  53. package/dist/commands/getting-started-steps.js +163 -0
  54. package/dist/commands/getting-started-steps.js.map +1 -0
  55. package/dist/commands/getting-started.d.ts +9 -0
  56. package/dist/commands/getting-started.d.ts.map +1 -0
  57. package/dist/commands/getting-started.js +44 -0
  58. package/dist/commands/getting-started.js.map +1 -0
  59. package/dist/commands/init.d.ts +9 -0
  60. package/dist/commands/init.d.ts.map +1 -0
  61. package/dist/commands/init.js +74 -0
  62. package/dist/commands/init.js.map +1 -0
  63. package/dist/commands/init.test.d.ts +2 -0
  64. package/dist/commands/init.test.d.ts.map +1 -0
  65. package/dist/commands/init.test.js +115 -0
  66. package/dist/commands/init.test.js.map +1 -0
  67. package/dist/commands/jobs.d.ts +11 -0
  68. package/dist/commands/jobs.d.ts.map +1 -0
  69. package/dist/commands/jobs.js +370 -0
  70. package/dist/commands/jobs.js.map +1 -0
  71. package/dist/commands/listings/create.d.ts +4 -0
  72. package/dist/commands/listings/create.d.ts.map +1 -0
  73. package/dist/commands/listings/create.js +133 -0
  74. package/dist/commands/listings/create.js.map +1 -0
  75. package/dist/commands/listings/delete.d.ts +4 -0
  76. package/dist/commands/listings/delete.d.ts.map +1 -0
  77. package/dist/commands/listings/delete.js +32 -0
  78. package/dist/commands/listings/delete.js.map +1 -0
  79. package/dist/commands/listings/docs.d.ts +4 -0
  80. package/dist/commands/listings/docs.d.ts.map +1 -0
  81. package/dist/commands/listings/docs.js +48 -0
  82. package/dist/commands/listings/docs.js.map +1 -0
  83. package/dist/commands/listings/headers.d.ts +4 -0
  84. package/dist/commands/listings/headers.d.ts.map +1 -0
  85. package/dist/commands/listings/headers.js +78 -0
  86. package/dist/commands/listings/headers.js.map +1 -0
  87. package/dist/commands/listings/helpers.d.ts +10 -0
  88. package/dist/commands/listings/helpers.d.ts.map +1 -0
  89. package/dist/commands/listings/helpers.js +64 -0
  90. package/dist/commands/listings/helpers.js.map +1 -0
  91. package/dist/commands/listings/index.d.ts +11 -0
  92. package/dist/commands/listings/index.d.ts.map +1 -0
  93. package/dist/commands/listings/index.js +46 -0
  94. package/dist/commands/listings/index.js.map +1 -0
  95. package/dist/commands/listings/list.d.ts +4 -0
  96. package/dist/commands/listings/list.d.ts.map +1 -0
  97. package/dist/commands/listings/list.js +42 -0
  98. package/dist/commands/listings/list.js.map +1 -0
  99. package/dist/commands/listings/pause-unpause.d.ts +6 -0
  100. package/dist/commands/listings/pause-unpause.d.ts.map +1 -0
  101. package/dist/commands/listings/pause-unpause.js +37 -0
  102. package/dist/commands/listings/pause-unpause.js.map +1 -0
  103. package/dist/commands/listings/rotate-key.d.ts +4 -0
  104. package/dist/commands/listings/rotate-key.d.ts.map +1 -0
  105. package/dist/commands/listings/rotate-key.js +62 -0
  106. package/dist/commands/listings/rotate-key.js.map +1 -0
  107. package/dist/commands/listings/update.d.ts +4 -0
  108. package/dist/commands/listings/update.d.ts.map +1 -0
  109. package/dist/commands/listings/update.js +57 -0
  110. package/dist/commands/listings/update.js.map +1 -0
  111. package/dist/commands/listings.d.ts +2 -0
  112. package/dist/commands/listings.d.ts.map +1 -0
  113. package/dist/commands/listings.js +3 -0
  114. package/dist/commands/listings.js.map +1 -0
  115. package/dist/commands/listings.test.d.ts +2 -0
  116. package/dist/commands/listings.test.d.ts.map +1 -0
  117. package/dist/commands/listings.test.js +166 -0
  118. package/dist/commands/listings.test.js.map +1 -0
  119. package/dist/commands/pricing.d.ts +9 -0
  120. package/dist/commands/pricing.d.ts.map +1 -0
  121. package/dist/commands/pricing.js +67 -0
  122. package/dist/commands/pricing.js.map +1 -0
  123. package/dist/commands/pricing.test.d.ts +2 -0
  124. package/dist/commands/pricing.test.d.ts.map +1 -0
  125. package/dist/commands/pricing.test.js +87 -0
  126. package/dist/commands/pricing.test.js.map +1 -0
  127. package/dist/commands/proxy.d.ts +14 -0
  128. package/dist/commands/proxy.d.ts.map +1 -0
  129. package/dist/commands/proxy.js +138 -0
  130. package/dist/commands/proxy.js.map +1 -0
  131. package/dist/commands/proxy.test.d.ts +2 -0
  132. package/dist/commands/proxy.test.d.ts.map +1 -0
  133. package/dist/commands/proxy.test.js +235 -0
  134. package/dist/commands/proxy.test.js.map +1 -0
  135. package/dist/commands/rate.d.ts +3 -0
  136. package/dist/commands/rate.d.ts.map +1 -0
  137. package/dist/commands/rate.js +46 -0
  138. package/dist/commands/rate.js.map +1 -0
  139. package/dist/commands/rate.test.d.ts +2 -0
  140. package/dist/commands/rate.test.d.ts.map +1 -0
  141. package/dist/commands/rate.test.js +74 -0
  142. package/dist/commands/rate.test.js.map +1 -0
  143. package/dist/commands/services.d.ts +3 -0
  144. package/dist/commands/services.d.ts.map +1 -0
  145. package/dist/commands/services.js +48 -0
  146. package/dist/commands/services.js.map +1 -0
  147. package/dist/commands/services.test.d.ts +2 -0
  148. package/dist/commands/services.test.d.ts.map +1 -0
  149. package/dist/commands/services.test.js +87 -0
  150. package/dist/commands/services.test.js.map +1 -0
  151. package/dist/commands/settlements.d.ts +3 -0
  152. package/dist/commands/settlements.d.ts.map +1 -0
  153. package/dist/commands/settlements.js +79 -0
  154. package/dist/commands/settlements.js.map +1 -0
  155. package/dist/commands/settlements.test.d.ts +2 -0
  156. package/dist/commands/settlements.test.d.ts.map +1 -0
  157. package/dist/commands/settlements.test.js +98 -0
  158. package/dist/commands/settlements.test.js.map +1 -0
  159. package/dist/commands/skills.d.ts +3 -0
  160. package/dist/commands/skills.d.ts.map +1 -0
  161. package/dist/commands/skills.js +96 -0
  162. package/dist/commands/skills.js.map +1 -0
  163. package/dist/commands/skills.test.d.ts +2 -0
  164. package/dist/commands/skills.test.d.ts.map +1 -0
  165. package/dist/commands/skills.test.js +89 -0
  166. package/dist/commands/skills.test.js.map +1 -0
  167. package/dist/commands/test-service.d.ts +3 -0
  168. package/dist/commands/test-service.d.ts.map +1 -0
  169. package/dist/commands/test-service.js +200 -0
  170. package/dist/commands/test-service.js.map +1 -0
  171. package/dist/commands/test-service.test.d.ts +2 -0
  172. package/dist/commands/test-service.test.d.ts.map +1 -0
  173. package/dist/commands/test-service.test.js +119 -0
  174. package/dist/commands/test-service.test.js.map +1 -0
  175. package/dist/commands/tunnel-handlers.d.ts +19 -0
  176. package/dist/commands/tunnel-handlers.d.ts.map +1 -0
  177. package/dist/commands/tunnel-handlers.js +103 -0
  178. package/dist/commands/tunnel-handlers.js.map +1 -0
  179. package/dist/commands/tunnel.d.ts +7 -0
  180. package/dist/commands/tunnel.d.ts.map +1 -0
  181. package/dist/commands/tunnel.js +184 -0
  182. package/dist/commands/tunnel.js.map +1 -0
  183. package/dist/commands/usage.d.ts +9 -0
  184. package/dist/commands/usage.d.ts.map +1 -0
  185. package/dist/commands/usage.js +78 -0
  186. package/dist/commands/usage.js.map +1 -0
  187. package/dist/commands/usage.test.d.ts +2 -0
  188. package/dist/commands/usage.test.d.ts.map +1 -0
  189. package/dist/commands/usage.test.js +102 -0
  190. package/dist/commands/usage.test.js.map +1 -0
  191. package/dist/commands/withdraw-confirm.d.ts +10 -0
  192. package/dist/commands/withdraw-confirm.d.ts.map +1 -0
  193. package/dist/commands/withdraw-confirm.js +51 -0
  194. package/dist/commands/withdraw-confirm.js.map +1 -0
  195. package/dist/commands/withdraw-confirm.test.d.ts +2 -0
  196. package/dist/commands/withdraw-confirm.test.d.ts.map +1 -0
  197. package/dist/commands/withdraw-confirm.test.js +75 -0
  198. package/dist/commands/withdraw-confirm.test.js.map +1 -0
  199. package/dist/commands/withdraw.d.ts +9 -0
  200. package/dist/commands/withdraw.d.ts.map +1 -0
  201. package/dist/commands/withdraw.js +78 -0
  202. package/dist/commands/withdraw.js.map +1 -0
  203. package/dist/commands/withdraw.test.d.ts +2 -0
  204. package/dist/commands/withdraw.test.d.ts.map +1 -0
  205. package/dist/commands/withdraw.test.js +96 -0
  206. package/dist/commands/withdraw.test.js.map +1 -0
  207. package/dist/config.d.ts +20 -0
  208. package/dist/config.d.ts.map +1 -0
  209. package/dist/config.js +38 -0
  210. package/dist/config.js.map +1 -0
  211. package/dist/config.test.d.ts +2 -0
  212. package/dist/config.test.d.ts.map +1 -0
  213. package/dist/config.test.js +54 -0
  214. package/dist/config.test.js.map +1 -0
  215. package/dist/format.d.ts +46 -0
  216. package/dist/format.d.ts.map +1 -0
  217. package/dist/format.js +101 -0
  218. package/dist/format.js.map +1 -0
  219. package/dist/format.test.d.ts +2 -0
  220. package/dist/format.test.d.ts.map +1 -0
  221. package/dist/format.test.js +85 -0
  222. package/dist/format.test.js.map +1 -0
  223. package/dist/generated/skills.d.ts +2 -0
  224. package/dist/generated/skills.d.ts.map +1 -0
  225. package/dist/generated/skills.js +23 -0
  226. package/dist/generated/skills.js.map +1 -0
  227. package/dist/helpers.d.ts +12 -0
  228. package/dist/helpers.d.ts.map +1 -0
  229. package/dist/helpers.js +21 -0
  230. package/dist/helpers.js.map +1 -0
  231. package/dist/helpers.test.d.ts +2 -0
  232. package/dist/helpers.test.d.ts.map +1 -0
  233. package/dist/helpers.test.js +59 -0
  234. package/dist/helpers.test.js.map +1 -0
  235. package/dist/index.d.ts +3 -0
  236. package/dist/index.d.ts.map +1 -0
  237. package/dist/index.js +70 -0
  238. package/dist/index.js.map +1 -0
  239. package/package.json +61 -0
  240. package/templates/http-api/README.md +20 -0
  241. package/templates/http-api/package.json +21 -0
  242. package/templates/http-api/proxygate.tunnel.yaml +9 -0
  243. package/templates/http-api/src/index.ts +77 -0
  244. package/templates/http-api/tsconfig.json +14 -0
  245. package/templates/llm-agent/README.md +23 -0
  246. package/templates/llm-agent/package.json +22 -0
  247. package/templates/llm-agent/proxygate.tunnel.yaml +13 -0
  248. package/templates/llm-agent/src/index.ts +142 -0
  249. package/templates/llm-agent/tsconfig.json +14 -0
@@ -0,0 +1,79 @@
1
+ import { ProxyGateError } from '@proxygate/sdk';
2
+ import { getClient } from '../helpers.js';
3
+ import { bold, green, red, dim, formatTable } from '../format.js';
4
+ function isBuyerSummary(s) {
5
+ return 'total_cost_usdc' in s;
6
+ }
7
+ function isBuyerDaily(d) {
8
+ return 'total_cost_usdc' in d;
9
+ }
10
+ export function registerSettlementsCommand(program) {
11
+ program
12
+ .command('settlements')
13
+ .description('View settlement history (buyer spend or seller earnings)')
14
+ .option('-r, --role <role>', 'Role: buyer or seller')
15
+ .option('-s, --service <name>', 'Filter by service')
16
+ .option('--from <date>', 'Start date (YYYY-MM-DD)')
17
+ .option('--to <date>', 'End date (YYYY-MM-DD)')
18
+ .option('-l, --limit <n>', 'Max entries')
19
+ .addHelpText('after', '\nExamples:\n' +
20
+ ' $ proxygate settlements\n' +
21
+ ' $ proxygate settlements --role seller\n' +
22
+ ' $ proxygate settlements --from 2026-03-01 --json')
23
+ .action(async (opts) => {
24
+ const parentOpts = program.opts();
25
+ try {
26
+ const client = await getClient(parentOpts);
27
+ const result = await client.settlements({
28
+ role: opts.role,
29
+ service: opts.service,
30
+ from: opts.from,
31
+ to: opts.to,
32
+ limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
33
+ });
34
+ if (parentOpts.json) {
35
+ console.log(JSON.stringify(result, null, 2));
36
+ return;
37
+ }
38
+ console.log(bold(`Settlement History (${result.role})`));
39
+ console.log(dim(`${result.date_range.from} to ${result.date_range.to}`));
40
+ console.log();
41
+ if (result.daily.length === 0) {
42
+ console.log(dim('No settlement data found.'));
43
+ return;
44
+ }
45
+ if (isBuyerSummary(result.summary)) {
46
+ console.log(` ${green('Requests:')} ${result.summary.total_requests}`);
47
+ console.log(` ${green('Total Cost:')} $${result.summary.total_cost_usdc}`);
48
+ console.log(` ${dim('Fees:')} $${result.summary.total_fees_usdc}`);
49
+ }
50
+ else {
51
+ console.log(` ${green('Requests:')} ${result.summary.total_requests}`);
52
+ console.log(` ${green('Earnings:')} $${result.summary.total_earnings_usdc}`);
53
+ console.log(` ${dim('Fees:')} $${result.summary.total_fees_usdc}`);
54
+ }
55
+ console.log();
56
+ const headers = isBuyerDaily(result.daily[0])
57
+ ? ['Date', 'Service', 'Requests', 'Cost', 'Fees', 'Net']
58
+ : ['Date', 'Service', 'Requests', 'Earnings', 'Fees', 'Payout'];
59
+ const rows = result.daily.map((d) => {
60
+ if (isBuyerDaily(d)) {
61
+ return [d.date, d.service, String(d.request_count), `$${d.total_cost_usdc}`, `$${d.total_fees_usdc}`, `$${d.net_spend_usdc}`];
62
+ }
63
+ const s = d;
64
+ return [s.date, s.service, String(s.request_count), `$${s.total_earnings_usdc}`, `$${s.total_fees_usdc}`, `$${s.net_payout_usdc}`];
65
+ });
66
+ console.log(formatTable(headers, rows));
67
+ }
68
+ catch (err) {
69
+ if (err instanceof ProxyGateError) {
70
+ console.error(red(`Error [${err.code}]: ${err.message}`));
71
+ if (err.action)
72
+ console.error(dim(`Suggestion: ${err.action}`));
73
+ process.exit(1);
74
+ }
75
+ throw err;
76
+ }
77
+ });
78
+ }
79
+ //# sourceMappingURL=settlements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlements.js","sourceRoot":"","sources":["../../src/commands/settlements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=settlements.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlements.test.d.ts","sourceRoot":"","sources":["../../src/commands/settlements.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerSettlementsCommand } from './settlements.js';
4
+ const mockSettlements = vi.fn();
5
+ vi.mock('@proxygate/sdk', () => ({
6
+ ProxyGateClient: {
7
+ create: vi.fn().mockResolvedValue({
8
+ settlements: (...args) => mockSettlements(...args),
9
+ }),
10
+ },
11
+ ProxyGateError: class extends Error {
12
+ code;
13
+ constructor(msg, code) {
14
+ super(msg);
15
+ this.code = code;
16
+ }
17
+ },
18
+ }));
19
+ vi.mock('../config.js', () => ({
20
+ loadConfig: vi.fn().mockResolvedValue({
21
+ gatewayUrl: 'http://localhost:3001',
22
+ keypairPath: '/tmp/key.json',
23
+ }),
24
+ }));
25
+ const BUYER_RESULT = {
26
+ role: 'buyer',
27
+ date_range: { from: '2026-03-01', to: '2026-03-05' },
28
+ daily: [
29
+ { date: '2026-03-01', service: 'openai', request_count: 50, total_cost_usdc: 0.5, total_fees_usdc: 0.025, net_spend_usdc: 0.525 },
30
+ ],
31
+ cursor: null,
32
+ has_more: false,
33
+ summary: { total_requests: 50, total_cost_usdc: 0.5, total_fees_usdc: 0.025 },
34
+ };
35
+ const SELLER_RESULT = {
36
+ role: 'seller',
37
+ date_range: { from: '2026-03-01', to: '2026-03-05' },
38
+ daily: [
39
+ { date: '2026-03-01', service: 'openai', request_count: 50, total_earnings_usdc: 0.475, total_fees_usdc: 0.025, net_payout_usdc: 0.45 },
40
+ ],
41
+ cursor: null,
42
+ has_more: false,
43
+ summary: { total_requests: 50, total_earnings_usdc: 0.475, total_fees_usdc: 0.025 },
44
+ };
45
+ describe('settlements command', () => {
46
+ let logSpy;
47
+ beforeEach(() => {
48
+ vi.clearAllMocks();
49
+ logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
50
+ vi.spyOn(console, 'error').mockImplementation(() => { });
51
+ });
52
+ afterEach(() => {
53
+ vi.restoreAllMocks();
54
+ });
55
+ const run = async (...args) => {
56
+ const program = new Command('proxygate');
57
+ program
58
+ .option('--gateway <url>')
59
+ .option('--keypair <path>')
60
+ .option('--json', 'Output raw JSON');
61
+ registerSettlementsCommand(program);
62
+ await program.parseAsync(['node', 'proxygate', 'settlements', ...args]);
63
+ };
64
+ it('outputs formatted buyer settlement', async () => {
65
+ mockSettlements.mockResolvedValue(BUYER_RESULT);
66
+ await run();
67
+ const output = logSpy.mock.calls.map((c) => c[0]).join('\n');
68
+ expect(output).toContain('Settlement History (buyer)');
69
+ expect(output).toContain('50');
70
+ expect(output).toContain('openai');
71
+ });
72
+ it('outputs formatted seller settlement', async () => {
73
+ mockSettlements.mockResolvedValue(SELLER_RESULT);
74
+ await run('--role', 'seller');
75
+ const output = logSpy.mock.calls.map((c) => c[0]).join('\n');
76
+ expect(output).toContain('Settlement History (seller)');
77
+ expect(output).toContain('Earnings');
78
+ });
79
+ it('outputs raw JSON with --json flag', async () => {
80
+ mockSettlements.mockResolvedValue(BUYER_RESULT);
81
+ await run('--json');
82
+ expect(logSpy).toHaveBeenCalledTimes(1);
83
+ const parsed = JSON.parse(logSpy.mock.calls[0][0]);
84
+ expect(parsed).toEqual(BUYER_RESULT);
85
+ });
86
+ it('passes filter options to client', async () => {
87
+ mockSettlements.mockResolvedValue({ ...BUYER_RESULT, daily: [] });
88
+ await run('--role', 'seller', '--service', 'openai', '--from', '2026-03-01');
89
+ expect(mockSettlements).toHaveBeenCalledWith(expect.objectContaining({ role: 'seller', service: 'openai', from: '2026-03-01' }));
90
+ });
91
+ it('shows empty message when no data', async () => {
92
+ mockSettlements.mockResolvedValue({ ...BUYER_RESULT, daily: [] });
93
+ await run();
94
+ const output = logSpy.mock.calls.map((c) => c[0]).join('\n');
95
+ expect(output).toContain('No settlement data found');
96
+ });
97
+ });
98
+ //# sourceMappingURL=settlements.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlements.test.js","sourceRoot":"","sources":["../../src/commands/settlements.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerSkillsCommand(program: Command): void;
3
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU5D"}
@@ -0,0 +1,96 @@
1
+ import { dirname, join } from 'node:path';
2
+ import { homedir } from 'node:os';
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { SKILLS } from '../generated/skills.js';
5
+ import { green, dim, yellow } from '../format.js';
6
+ const HOOK_SCRIPT_PATH = join(homedir(), '.claude', 'skills', 'pg-update', 'scripts', 'check-update.sh');
7
+ const HOOK_MARKER = 'proxygate-update-check';
8
+ export function registerSkillsCommand(program) {
9
+ const skills = program
10
+ .command('skills')
11
+ .description('Manage Claude Code skills for ProxyGate');
12
+ skills
13
+ .command('install')
14
+ .description('Install ProxyGate skills for Claude Code (writes to ~/.claude/skills/)')
15
+ .option('--json', 'JSON output')
16
+ .action(installSkills);
17
+ }
18
+ async function installSkills(options) {
19
+ const json = !!options.json;
20
+ const skillNames = Object.keys(SKILLS);
21
+ if (skillNames.length === 0) {
22
+ const msg = 'No skills bundled in this build. Rebuild with: pnpm build';
23
+ if (json) {
24
+ console.log(JSON.stringify({ error: msg }));
25
+ }
26
+ else {
27
+ console.error(msg);
28
+ }
29
+ process.exit(1);
30
+ }
31
+ const baseDir = join(homedir(), '.claude', 'skills');
32
+ const installed = [];
33
+ for (const [skillName, files] of Object.entries(SKILLS)) {
34
+ const skillDir = join(baseDir, skillName);
35
+ const writtenFiles = [];
36
+ for (const [relativePath, content] of Object.entries(files)) {
37
+ const fullPath = join(skillDir, relativePath);
38
+ await mkdir(dirname(fullPath), { recursive: true });
39
+ await writeFile(fullPath, content, 'utf-8');
40
+ writtenFiles.push(relativePath);
41
+ }
42
+ installed.push({ name: skillName, files: writtenFiles });
43
+ }
44
+ const hookRegistered = await registerUpdateHook();
45
+ if (json) {
46
+ console.log(JSON.stringify({ installed, path: baseDir, hookRegistered }));
47
+ return;
48
+ }
49
+ console.log();
50
+ for (const skill of installed) {
51
+ console.log(` ${green('+')} ${skill.name} ${dim(`(${skill.files.length} files)`)}`);
52
+ }
53
+ if (hookRegistered) {
54
+ console.log(` ${green('+')} SessionStart hook ${dim('(update checker)')}`);
55
+ }
56
+ console.log();
57
+ console.log(green(`Installed ${installed.length} skills to ${baseDir}`));
58
+ }
59
+ async function registerUpdateHook() {
60
+ const settingsPath = join(homedir(), '.claude', 'settings.json');
61
+ try {
62
+ let settings = {};
63
+ try {
64
+ const raw = await readFile(settingsPath, 'utf-8');
65
+ settings = JSON.parse(raw);
66
+ }
67
+ catch {
68
+ // No existing settings — start fresh
69
+ }
70
+ const hooks = (settings.hooks ?? {});
71
+ const sessionStartEntries = (hooks.SessionStart ?? []);
72
+ const alreadyRegistered = sessionStartEntries.some((entry) => entry.hooks?.some((h) => h.command?.includes(HOOK_MARKER) ||
73
+ h.command?.includes('check-update.sh')));
74
+ if (!alreadyRegistered) {
75
+ sessionStartEntries.push({
76
+ matcher: '',
77
+ hooks: [
78
+ {
79
+ type: 'command',
80
+ command: `bash "${HOOK_SCRIPT_PATH}" # ${HOOK_MARKER}`,
81
+ },
82
+ ],
83
+ });
84
+ hooks.SessionStart = sessionStartEntries;
85
+ settings.hooks = hooks;
86
+ }
87
+ await mkdir(dirname(settingsPath), { recursive: true });
88
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
89
+ return true;
90
+ }
91
+ catch (error) {
92
+ console.error(yellow(' ! Could not register update hook:'), error.message);
93
+ return false;
94
+ }
95
+ }
96
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,gBAAgB,GAAG,IAAI,CAC3B,OAAO,EAAE,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,SAAS,EACT,iBAAiB,CAClB,CAAC;AACF,MAAM,WAAW,GAAG,wBAAwB,CAAC;AAE7C,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,MAAM,GAAG,OAAO;SACnB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,yCAAyC,CAAC,CAAC;IAE1D,MAAM;SACH,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,wEAAwE,CAAC;SACrF,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;SAC/B,MAAM,CAAC,aAAa,CAAC,CAAC;AAC3B,CAAC;AAOD,KAAK,UAAU,aAAa,CAAC,OAA2B;IACtD,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC5B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,2DAA2D,CAAC;QACxE,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,SAAS,GAAgB,EAAE,CAAC;IAElC,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC1C,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YAC9C,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5C,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClC,CAAC;QAED,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAElD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QAC1E,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,sBAAsB,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,SAAS,CAAC,MAAM,cAAc,OAAO,EAAE,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IAEjE,IAAI,CAAC;QACH,IAAI,QAAQ,GAA4B,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YAClD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAA8B,CAAC;QAClE,MAAM,mBAAmB,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAGnD,CAAC;QAEH,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3D,KAAK,CAAC,KAAK,EAAE,IAAI,CACf,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC;YAChC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CACzC,CACF,CAAC;QAEF,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,mBAAmB,CAAC,IAAI,CAAC;gBACvB,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE;oBACL;wBACE,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,SAAS,gBAAgB,OAAO,WAAW,EAAE;qBACvD;iBACF;aACF,CAAC,CAAC;YACH,KAAK,CAAC,YAAY,GAAG,mBAAmB,CAAC;YACzC,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACzB,CAAC;QAED,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,MAAM,CAAC,qCAAqC,CAAC,EAC5C,KAAe,CAAC,OAAO,CACzB,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skills.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.test.d.ts","sourceRoot":"","sources":["../../src/commands/skills.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { join } from 'node:path';
3
+ import { mkdtemp, rm, readFile, writeFile, mkdir } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ let tempDir;
6
+ vi.mock('node:os', async () => {
7
+ const actual = await vi.importActual('node:os');
8
+ return { ...actual, homedir: () => tempDir };
9
+ });
10
+ vi.mock('../generated/skills.js', () => ({
11
+ SKILLS: {
12
+ 'test-skill': {
13
+ 'SKILL.md': '---\nname: test-skill\n---\n# Test',
14
+ 'references/commands.md': '# Commands\n\ntest content',
15
+ },
16
+ 'test-skill-2': {
17
+ 'SKILL.md': '---\nname: test-skill-2\n---\n# Test 2',
18
+ },
19
+ },
20
+ }));
21
+ describe('skills install', () => {
22
+ beforeEach(async () => {
23
+ tempDir = await mkdtemp(join(tmpdir(), 'pg-skills-test-'));
24
+ });
25
+ afterEach(async () => {
26
+ await rm(tempDir, { recursive: true, force: true });
27
+ vi.resetModules();
28
+ });
29
+ it('writes skill files to ~/.claude/skills/', async () => {
30
+ const { SKILLS } = await import('../generated/skills.js');
31
+ const baseDir = join(tempDir, '.claude', 'skills');
32
+ const { dirname } = await import('node:path');
33
+ for (const [skillName, files] of Object.entries(SKILLS)) {
34
+ for (const [relPath, content] of Object.entries(files)) {
35
+ const fullPath = join(baseDir, skillName, relPath);
36
+ await mkdir(dirname(fullPath), { recursive: true });
37
+ await writeFile(fullPath, content, 'utf-8');
38
+ }
39
+ }
40
+ const skillMd = await readFile(join(baseDir, 'test-skill', 'SKILL.md'), 'utf-8');
41
+ expect(skillMd).toContain('name: test-skill');
42
+ const cmdRef = await readFile(join(baseDir, 'test-skill', 'references', 'commands.md'), 'utf-8');
43
+ expect(cmdRef).toContain('test content');
44
+ const skill2 = await readFile(join(baseDir, 'test-skill-2', 'SKILL.md'), 'utf-8');
45
+ expect(skill2).toContain('name: test-skill-2');
46
+ });
47
+ it('registers SessionStart hook in settings.json', async () => {
48
+ const settingsDir = join(tempDir, '.claude');
49
+ await mkdir(settingsDir, { recursive: true });
50
+ await writeFile(join(settingsDir, 'settings.json'), '{}', 'utf-8');
51
+ const settingsPath = join(settingsDir, 'settings.json');
52
+ const raw = await readFile(settingsPath, 'utf-8');
53
+ const settings = JSON.parse(raw);
54
+ settings.hooks = {
55
+ SessionStart: [{
56
+ matcher: '',
57
+ hooks: [{
58
+ type: 'command',
59
+ command: 'bash "~/.claude/skills/pg-update/scripts/check-update.sh" # proxygate-update-check',
60
+ }],
61
+ }],
62
+ };
63
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
64
+ const result = JSON.parse(await readFile(settingsPath, 'utf-8'));
65
+ expect(result.hooks.SessionStart).toHaveLength(1);
66
+ expect(result.hooks.SessionStart[0].hooks[0].command).toContain('proxygate-update-check');
67
+ });
68
+ it('detects existing hook to avoid duplication', async () => {
69
+ const existing = {
70
+ hooks: {
71
+ SessionStart: [{
72
+ matcher: '',
73
+ hooks: [{ type: 'command', command: 'bash "..." # proxygate-update-check' }],
74
+ }],
75
+ },
76
+ };
77
+ const entries = existing.hooks.SessionStart;
78
+ const alreadyRegistered = entries.some((e) => e.hooks?.some((h) => h.command?.includes('proxygate-update-check')));
79
+ expect(alreadyRegistered).toBe(true);
80
+ });
81
+ it('detects when hook is not registered', () => {
82
+ const entries = [
83
+ { matcher: '', hooks: [{ type: 'command', command: 'bash other-script.sh' }] },
84
+ ];
85
+ const alreadyRegistered = entries.some((e) => e.hooks?.some((h) => h.command?.includes('proxygate-update-check')));
86
+ expect(alreadyRegistered).toBe(false);
87
+ });
88
+ });
89
+ //# sourceMappingURL=skills.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.test.js","sourceRoot":"","sources":["../../src/commands/skills.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerTestCommand(program: Command): void;
3
+ //# sourceMappingURL=test-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-service.d.ts","sourceRoot":"","sources":["../../src/commands/test-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiJzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoH1D"}
@@ -0,0 +1,200 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import yaml from 'js-yaml';
4
+ import { bold, green, yellow, red, dim } from '../format.js';
5
+ async function testEndpoint(port, method, path, payload) {
6
+ const url = `http://localhost:${port}${path}`;
7
+ const start = performance.now();
8
+ try {
9
+ const controller = new AbortController();
10
+ const timeout = setTimeout(() => controller.abort(), 10_000);
11
+ const init = {
12
+ method,
13
+ signal: controller.signal,
14
+ headers: { 'content-type': 'application/json' },
15
+ };
16
+ if (payload && method !== 'GET' && method !== 'HEAD') {
17
+ init.body = payload;
18
+ }
19
+ else if (method === 'POST') {
20
+ // Send minimal test body for POST endpoints
21
+ init.body = JSON.stringify({ test: true });
22
+ }
23
+ const res = await fetch(url, init);
24
+ clearTimeout(timeout);
25
+ const latencyMs = Math.round(performance.now() - start);
26
+ const contentType = res.headers.get('content-type') ?? '';
27
+ const isSSE = contentType.includes('text/event-stream');
28
+ // Consume body to validate it
29
+ if (isSSE) {
30
+ const text = await res.text();
31
+ const chunks = text.split('\n\n').filter((c) => c.trim().startsWith('data:'));
32
+ return {
33
+ endpoint: `${method} ${path}`,
34
+ method,
35
+ status: res.status,
36
+ latencyMs,
37
+ contentType,
38
+ isSSE: true,
39
+ error: chunks.length === 0 ? 'SSE stream had no data chunks' : null,
40
+ };
41
+ }
42
+ else {
43
+ // Try to parse as JSON to validate
44
+ const text = await res.text();
45
+ if (contentType.includes('application/json')) {
46
+ try {
47
+ JSON.parse(text);
48
+ }
49
+ catch {
50
+ return {
51
+ endpoint: `${method} ${path}`,
52
+ method,
53
+ status: res.status,
54
+ latencyMs,
55
+ contentType,
56
+ isSSE: false,
57
+ error: 'Response has application/json content-type but body is not valid JSON',
58
+ };
59
+ }
60
+ }
61
+ return {
62
+ endpoint: `${method} ${path}`,
63
+ method,
64
+ status: res.status,
65
+ latencyMs,
66
+ contentType,
67
+ isSSE: false,
68
+ error: null,
69
+ };
70
+ }
71
+ }
72
+ catch (err) {
73
+ const latencyMs = Math.round(performance.now() - start);
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ const hint = message.includes('ECONNREFUSED') || message.includes('fetch failed')
76
+ ? ` — is your server running on port ${port}?`
77
+ : '';
78
+ return {
79
+ endpoint: `${method} ${path}`,
80
+ method,
81
+ status: null,
82
+ latencyMs,
83
+ contentType: '',
84
+ isSSE: false,
85
+ error: `${message}${hint}`,
86
+ };
87
+ }
88
+ }
89
+ function printResult(result) {
90
+ const statusStr = result.status !== null
91
+ ? result.status < 400
92
+ ? green(`${result.status}`)
93
+ : red(`${result.status}`)
94
+ : red('ERR');
95
+ console.log(` ${bold(result.endpoint)}`);
96
+ if (result.error) {
97
+ console.log(` ${red('FAIL')} ${result.error}`);
98
+ }
99
+ else {
100
+ console.log(` ${green('OK')} ${statusStr} (${result.latencyMs}ms)`);
101
+ if (result.isSSE) {
102
+ console.log(` ${green('OK')} Valid SSE stream`);
103
+ }
104
+ else if (result.contentType.includes('application/json')) {
105
+ console.log(` ${green('OK')} Valid JSON response`);
106
+ }
107
+ }
108
+ console.log();
109
+ }
110
+ export function registerTestCommand(program) {
111
+ program
112
+ .command('test')
113
+ .description('Test local services defined in proxygate.tunnel.yaml')
114
+ .option('-c, --config <path>', 'Path to tunnel YAML config', 'proxygate.tunnel.yaml')
115
+ .option('--payload <json>', 'Custom JSON payload for POST endpoints')
116
+ .option('--endpoint <spec>', 'Test a single endpoint (e.g., "POST /v1/analyze")')
117
+ .addHelpText('after', '\nExamples:\n' +
118
+ ' $ proxygate test\n' +
119
+ ' $ proxygate test -c my-services.yaml\n' +
120
+ ' $ proxygate test --endpoint "POST /v1/analyze" --payload \'{"code":"x=1"}\'\n')
121
+ .action(async (opts) => {
122
+ try {
123
+ // Load tunnel config
124
+ const configPath = resolve(opts.config);
125
+ let yamlContent;
126
+ try {
127
+ yamlContent = await readFile(configPath, 'utf-8');
128
+ }
129
+ catch {
130
+ console.error(red(`Cannot read config: ${configPath}`));
131
+ console.error(dim('Create a proxygate.tunnel.yaml or use -c <path>.'));
132
+ process.exit(1);
133
+ }
134
+ const config = yaml.load(yamlContent);
135
+ if (!config?.services?.length) {
136
+ console.error(red('No services defined in config.'));
137
+ process.exit(1);
138
+ }
139
+ console.log();
140
+ console.log(dim(`Testing services from ${opts.config}...`));
141
+ console.log();
142
+ let totalTests = 0;
143
+ let passed = 0;
144
+ let failed = 0;
145
+ for (const svc of config.services) {
146
+ console.log(`${bold(`── ${svc.name}`)} ${dim(`(localhost:${svc.port})`)}`);
147
+ console.log();
148
+ // Determine endpoints to test
149
+ let endpoints;
150
+ if (opts.endpoint) {
151
+ // Parse --endpoint flag: "POST /v1/analyze"
152
+ const [method, ...pathParts] = opts.endpoint.split(' ');
153
+ endpoints = [{ method: method ?? 'GET', path: pathParts.join(' ') || '/' }];
154
+ }
155
+ else if (svc.endpoints?.length) {
156
+ endpoints = svc.endpoints;
157
+ }
158
+ else {
159
+ // Fallback: health check
160
+ endpoints = [{ method: 'GET', path: '/' }];
161
+ }
162
+ for (const ep of endpoints) {
163
+ const result = await testEndpoint(svc.port, ep.method, ep.path, opts.payload);
164
+ printResult(result);
165
+ totalTests++;
166
+ if (result.error || (result.status !== null && result.status >= 400)) {
167
+ failed++;
168
+ }
169
+ else {
170
+ passed++;
171
+ }
172
+ }
173
+ // Warn if no docs
174
+ if (!svc.docs) {
175
+ console.log(` ${yellow('!')} No docs file configured — buyers won't see endpoint documentation`);
176
+ console.log(dim(' Add `docs: ./openapi.yaml` to your tunnel config'));
177
+ console.log();
178
+ }
179
+ }
180
+ // Summary
181
+ console.log('─'.repeat(40));
182
+ if (failed === 0) {
183
+ console.log(green(`All ${totalTests} endpoint${totalTests === 1 ? '' : 's'} passed.`) +
184
+ ' Ready to go live:');
185
+ console.log(` ${bold('proxygate tunnel')}`);
186
+ }
187
+ else {
188
+ console.log(red(`${failed}/${totalTests} endpoint${totalTests === 1 ? '' : 's'} failed.`));
189
+ process.exit(1);
190
+ }
191
+ console.log();
192
+ }
193
+ catch (err) {
194
+ const message = err instanceof Error ? err.message : String(err);
195
+ console.error(red(`Error: ${message}`));
196
+ process.exit(1);
197
+ }
198
+ });
199
+ }
200
+ //# sourceMappingURL=test-service.js.map