@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.
- package/LICENSE +17 -0
- package/README.md +311 -0
- package/dist/commands/apis.d.ts +3 -0
- package/dist/commands/apis.d.ts.map +1 -0
- package/dist/commands/apis.js +68 -0
- package/dist/commands/apis.js.map +1 -0
- package/dist/commands/apis.test.d.ts +2 -0
- package/dist/commands/apis.test.d.ts.map +1 -0
- package/dist/commands/apis.test.js +103 -0
- package/dist/commands/apis.test.js.map +1 -0
- package/dist/commands/balance.d.ts +9 -0
- package/dist/commands/balance.d.ts.map +1 -0
- package/dist/commands/balance.js +50 -0
- package/dist/commands/balance.js.map +1 -0
- package/dist/commands/balance.test.d.ts +2 -0
- package/dist/commands/balance.test.d.ts.map +1 -0
- package/dist/commands/balance.test.js +106 -0
- package/dist/commands/balance.test.js.map +1 -0
- package/dist/commands/categories.d.ts +3 -0
- package/dist/commands/categories.d.ts.map +1 -0
- package/dist/commands/categories.js +45 -0
- package/dist/commands/categories.js.map +1 -0
- package/dist/commands/categories.test.d.ts +2 -0
- package/dist/commands/categories.test.d.ts.map +1 -0
- package/dist/commands/categories.test.js +84 -0
- package/dist/commands/categories.test.js.map +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +136 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/create.test.d.ts +2 -0
- package/dist/commands/create.test.d.ts.map +1 -0
- package/dist/commands/create.test.js +95 -0
- package/dist/commands/create.test.js.map +1 -0
- package/dist/commands/deposit.d.ts +9 -0
- package/dist/commands/deposit.d.ts.map +1 -0
- package/dist/commands/deposit.js +78 -0
- package/dist/commands/deposit.js.map +1 -0
- package/dist/commands/deposit.test.d.ts +2 -0
- package/dist/commands/deposit.test.d.ts.map +1 -0
- package/dist/commands/deposit.test.js +93 -0
- package/dist/commands/deposit.test.js.map +1 -0
- package/dist/commands/dev.d.ts +3 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +188 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/dev.test.d.ts +2 -0
- package/dist/commands/dev.test.d.ts.map +1 -0
- package/dist/commands/dev.test.js +89 -0
- package/dist/commands/dev.test.js.map +1 -0
- package/dist/commands/getting-started-steps.d.ts +12 -0
- package/dist/commands/getting-started-steps.d.ts.map +1 -0
- package/dist/commands/getting-started-steps.js +163 -0
- package/dist/commands/getting-started-steps.js.map +1 -0
- package/dist/commands/getting-started.d.ts +9 -0
- package/dist/commands/getting-started.d.ts.map +1 -0
- package/dist/commands/getting-started.js +44 -0
- package/dist/commands/getting-started.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +74 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +115 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/jobs.d.ts +11 -0
- package/dist/commands/jobs.d.ts.map +1 -0
- package/dist/commands/jobs.js +370 -0
- package/dist/commands/jobs.js.map +1 -0
- package/dist/commands/listings/create.d.ts +4 -0
- package/dist/commands/listings/create.d.ts.map +1 -0
- package/dist/commands/listings/create.js +133 -0
- package/dist/commands/listings/create.js.map +1 -0
- package/dist/commands/listings/delete.d.ts +4 -0
- package/dist/commands/listings/delete.d.ts.map +1 -0
- package/dist/commands/listings/delete.js +32 -0
- package/dist/commands/listings/delete.js.map +1 -0
- package/dist/commands/listings/docs.d.ts +4 -0
- package/dist/commands/listings/docs.d.ts.map +1 -0
- package/dist/commands/listings/docs.js +48 -0
- package/dist/commands/listings/docs.js.map +1 -0
- package/dist/commands/listings/headers.d.ts +4 -0
- package/dist/commands/listings/headers.d.ts.map +1 -0
- package/dist/commands/listings/headers.js +78 -0
- package/dist/commands/listings/headers.js.map +1 -0
- package/dist/commands/listings/helpers.d.ts +10 -0
- package/dist/commands/listings/helpers.d.ts.map +1 -0
- package/dist/commands/listings/helpers.js +64 -0
- package/dist/commands/listings/helpers.js.map +1 -0
- package/dist/commands/listings/index.d.ts +11 -0
- package/dist/commands/listings/index.d.ts.map +1 -0
- package/dist/commands/listings/index.js +46 -0
- package/dist/commands/listings/index.js.map +1 -0
- package/dist/commands/listings/list.d.ts +4 -0
- package/dist/commands/listings/list.d.ts.map +1 -0
- package/dist/commands/listings/list.js +42 -0
- package/dist/commands/listings/list.js.map +1 -0
- package/dist/commands/listings/pause-unpause.d.ts +6 -0
- package/dist/commands/listings/pause-unpause.d.ts.map +1 -0
- package/dist/commands/listings/pause-unpause.js +37 -0
- package/dist/commands/listings/pause-unpause.js.map +1 -0
- package/dist/commands/listings/rotate-key.d.ts +4 -0
- package/dist/commands/listings/rotate-key.d.ts.map +1 -0
- package/dist/commands/listings/rotate-key.js +62 -0
- package/dist/commands/listings/rotate-key.js.map +1 -0
- package/dist/commands/listings/update.d.ts +4 -0
- package/dist/commands/listings/update.d.ts.map +1 -0
- package/dist/commands/listings/update.js +57 -0
- package/dist/commands/listings/update.js.map +1 -0
- package/dist/commands/listings.d.ts +2 -0
- package/dist/commands/listings.d.ts.map +1 -0
- package/dist/commands/listings.js +3 -0
- package/dist/commands/listings.js.map +1 -0
- package/dist/commands/listings.test.d.ts +2 -0
- package/dist/commands/listings.test.d.ts.map +1 -0
- package/dist/commands/listings.test.js +166 -0
- package/dist/commands/listings.test.js.map +1 -0
- package/dist/commands/pricing.d.ts +9 -0
- package/dist/commands/pricing.d.ts.map +1 -0
- package/dist/commands/pricing.js +67 -0
- package/dist/commands/pricing.js.map +1 -0
- package/dist/commands/pricing.test.d.ts +2 -0
- package/dist/commands/pricing.test.d.ts.map +1 -0
- package/dist/commands/pricing.test.js +87 -0
- package/dist/commands/pricing.test.js.map +1 -0
- package/dist/commands/proxy.d.ts +14 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +138 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/proxy.test.d.ts +2 -0
- package/dist/commands/proxy.test.d.ts.map +1 -0
- package/dist/commands/proxy.test.js +235 -0
- package/dist/commands/proxy.test.js.map +1 -0
- package/dist/commands/rate.d.ts +3 -0
- package/dist/commands/rate.d.ts.map +1 -0
- package/dist/commands/rate.js +46 -0
- package/dist/commands/rate.js.map +1 -0
- package/dist/commands/rate.test.d.ts +2 -0
- package/dist/commands/rate.test.d.ts.map +1 -0
- package/dist/commands/rate.test.js +74 -0
- package/dist/commands/rate.test.js.map +1 -0
- package/dist/commands/services.d.ts +3 -0
- package/dist/commands/services.d.ts.map +1 -0
- package/dist/commands/services.js +48 -0
- package/dist/commands/services.js.map +1 -0
- package/dist/commands/services.test.d.ts +2 -0
- package/dist/commands/services.test.d.ts.map +1 -0
- package/dist/commands/services.test.js +87 -0
- package/dist/commands/services.test.js.map +1 -0
- package/dist/commands/settlements.d.ts +3 -0
- package/dist/commands/settlements.d.ts.map +1 -0
- package/dist/commands/settlements.js +79 -0
- package/dist/commands/settlements.js.map +1 -0
- package/dist/commands/settlements.test.d.ts +2 -0
- package/dist/commands/settlements.test.d.ts.map +1 -0
- package/dist/commands/settlements.test.js +98 -0
- package/dist/commands/settlements.test.js.map +1 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +96 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/skills.test.d.ts +2 -0
- package/dist/commands/skills.test.d.ts.map +1 -0
- package/dist/commands/skills.test.js +89 -0
- package/dist/commands/skills.test.js.map +1 -0
- package/dist/commands/test-service.d.ts +3 -0
- package/dist/commands/test-service.d.ts.map +1 -0
- package/dist/commands/test-service.js +200 -0
- package/dist/commands/test-service.js.map +1 -0
- package/dist/commands/test-service.test.d.ts +2 -0
- package/dist/commands/test-service.test.d.ts.map +1 -0
- package/dist/commands/test-service.test.js +119 -0
- package/dist/commands/test-service.test.js.map +1 -0
- package/dist/commands/tunnel-handlers.d.ts +19 -0
- package/dist/commands/tunnel-handlers.d.ts.map +1 -0
- package/dist/commands/tunnel-handlers.js +103 -0
- package/dist/commands/tunnel-handlers.js.map +1 -0
- package/dist/commands/tunnel.d.ts +7 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +184 -0
- package/dist/commands/tunnel.js.map +1 -0
- package/dist/commands/usage.d.ts +9 -0
- package/dist/commands/usage.d.ts.map +1 -0
- package/dist/commands/usage.js +78 -0
- package/dist/commands/usage.js.map +1 -0
- package/dist/commands/usage.test.d.ts +2 -0
- package/dist/commands/usage.test.d.ts.map +1 -0
- package/dist/commands/usage.test.js +102 -0
- package/dist/commands/usage.test.js.map +1 -0
- package/dist/commands/withdraw-confirm.d.ts +10 -0
- package/dist/commands/withdraw-confirm.d.ts.map +1 -0
- package/dist/commands/withdraw-confirm.js +51 -0
- package/dist/commands/withdraw-confirm.js.map +1 -0
- package/dist/commands/withdraw-confirm.test.d.ts +2 -0
- package/dist/commands/withdraw-confirm.test.d.ts.map +1 -0
- package/dist/commands/withdraw-confirm.test.js +75 -0
- package/dist/commands/withdraw-confirm.test.js.map +1 -0
- package/dist/commands/withdraw.d.ts +9 -0
- package/dist/commands/withdraw.d.ts.map +1 -0
- package/dist/commands/withdraw.js +78 -0
- package/dist/commands/withdraw.js.map +1 -0
- package/dist/commands/withdraw.test.d.ts +2 -0
- package/dist/commands/withdraw.test.d.ts.map +1 -0
- package/dist/commands/withdraw.test.js +96 -0
- package/dist/commands/withdraw.test.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +54 -0
- package/dist/config.test.js.map +1 -0
- package/dist/format.d.ts +46 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +101 -0
- package/dist/format.js.map +1 -0
- package/dist/format.test.d.ts +2 -0
- package/dist/format.test.d.ts.map +1 -0
- package/dist/format.test.js +85 -0
- package/dist/format.test.js.map +1 -0
- package/dist/generated/skills.d.ts +2 -0
- package/dist/generated/skills.d.ts.map +1 -0
- package/dist/generated/skills.js +23 -0
- package/dist/generated/skills.js.map +1 -0
- package/dist/helpers.d.ts +12 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +21 -0
- package/dist/helpers.js.map +1 -0
- package/dist/helpers.test.d.ts +2 -0
- package/dist/helpers.test.d.ts.map +1 -0
- package/dist/helpers.test.js +59 -0
- package/dist/helpers.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
- package/templates/http-api/README.md +20 -0
- package/templates/http-api/package.json +21 -0
- package/templates/http-api/proxygate.tunnel.yaml +9 -0
- package/templates/http-api/src/index.ts +77 -0
- package/templates/http-api/tsconfig.json +14 -0
- package/templates/llm-agent/README.md +23 -0
- package/templates/llm-agent/package.json +22 -0
- package/templates/llm-agent/proxygate.tunnel.yaml +13 -0
- package/templates/llm-agent/src/index.ts +142 -0
- 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":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAElE,SAAS,cAAc,CAAC,CAAoB;IAC1C,OAAO,iBAAiB,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,YAAY,CAAC,CAAkB;IACtC,OAAO,iBAAiB,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,OAAgB;IACzD,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,0DAA0D,CAAC;SACvE,MAAM,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;SACpD,MAAM,CAAC,sBAAsB,EAAE,mBAAmB,CAAC;SACnD,MAAM,CAAC,eAAe,EAAE,yBAAyB,CAAC;SAClD,MAAM,CAAC,aAAa,EAAE,uBAAuB,CAAC;SAC9C,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;SACxC,WAAW,CACV,OAAO,EACP,eAAe;QACb,6BAA6B;QAC7B,2CAA2C;QAC3C,oDAAoD,CACvD;SACA,MAAM,CAAC,KAAK,EAAE,IAAqF,EAAE,EAAE;QACtG,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAA0D,CAAC;QAE1F,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;gBACtC,IAAI,EAAE,IAAI,CAAC,IAAsC;gBACjD,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;aACzD,CAAC,CAAC;YAEH,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,OAAO,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,EAAE,CAAC;YAEd,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,IAAI,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;gBAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,aAAa,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;gBAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,YAAY,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;YAC7E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;gBAC3E,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,WAAW,CAAC,QAAQ,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;gBACjF,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,aAAa,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,CAAC;YAEd,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC3C,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;gBACxD,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAElE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAClC,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;oBACpB,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC,eAAe,EAAE,EAAE,IAAI,CAAC,CAAC,eAAe,EAAE,EAAE,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC;gBAChI,CAAC;gBACD,MAAM,CAAC,GAAG,CAA4I,CAAC;gBACvJ,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC,mBAAmB,EAAE,EAAE,IAAI,CAAC,CAAC,eAAe,EAAE,EAAE,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;YACrI,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;gBAClC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC1D,IAAI,GAAG,CAAC,MAAM;oBAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -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 @@
|
|
|
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":"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"}
|
|
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,IAAI,OAAe,CAAC;AAEpB,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;IAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,YAAY,CAA2B,SAAS,CAAC,CAAC;IAC1E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,EAAE;QACN,YAAY,EAAE;YACZ,UAAU,EAAE,oCAAoC;YAChD,wBAAwB,EAAE,4BAA4B;SACvD;QACD,cAAc,EAAE;YACd,UAAU,EAAE,wCAAwC;SACrD;KACF;CACF,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,EAAE,CAAC,YAAY,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACnD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QAE9C,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACxD,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;gBACnD,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QACjF,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAE9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC;QACjG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAClF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAEnE,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,QAAQ,CAAC,KAAK,GAAG;YACf,YAAY,EAAE,CAAC;oBACb,OAAO,EAAE,EAAE;oBACX,KAAK,EAAE,CAAC;4BACN,IAAI,EAAE,SAAS;4BACf,OAAO,EAAE,oFAAoF;yBAC9F,CAAC;iBACH,CAAC;SACH,CAAC;QACF,MAAM,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QAEjF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,QAAQ,GAAG;YACf,KAAK,EAAE;gBACL,YAAY,EAAE,CAAC;wBACb,OAAO,EAAE,EAAE;wBACX,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;qBAC7E,CAAC;aACH;SACF,CAAC;QAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC;QAC5C,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3C,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC,CACpE,CAAC;QAEF,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,OAAO,GAAG;YACd,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,EAAE;SAC/E,CAAC;QACF,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3C,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC,CACpE,CAAC;QAEF,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -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
|