paygate-mcp 7.7.0 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,6 +104,8 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
|
|
|
104
104
|
- **Tool Stats** — `GET /tools/stats` per-tool analytics: call counts, success rate, avg/p95 latency, credits consumed, deny reason breakdown, top 10 consumers — optional `?tool=` for detailed single-tool view, `?since=` filter
|
|
105
105
|
- **Request Log Export** — `GET /requests/export` exports the full request log as JSON or CSV with Content-Disposition headers — filter by key/tool/status/since/until, combined time-window queries, no pagination limit
|
|
106
106
|
- **Tool Call Dry Run** — `POST /requests/dry-run` simulates a tool call without executing — checks key validity, ACL, rate limits, credits, and spending limits, returns predicted outcome with credits-after calculation and rate limit status
|
|
107
|
+
- **Batch Dry Run** — `POST /requests/dry-run/batch` simulates multiple tool calls at once — aggregate credit check, per-tool ACL validation, spending limit, returns per-tool results with total credits required and credits-after
|
|
108
|
+
- **Tool Availability** — `GET /tools/available?key=...` returns per-key tool availability with pricing, affordability (canAfford), ACL enforcement (accessible/denyReason), and per-tool + global rate limit status
|
|
107
109
|
- **Config Hot Reload** — `POST /config/reload` reloads pricing, rate limits, webhooks, quotas, and behavior flags from config file without server restart
|
|
108
110
|
- **Webhook Events** — POST batched usage events to any URL for external billing/alerting
|
|
109
111
|
- **Config File Mode** — Load all settings from a JSON file (`--config`)
|
|
@@ -2273,6 +2275,61 @@ curl -X POST http://localhost:3402/requests/dry-run \
|
|
|
2273
2275
|
|
|
2274
2276
|
Checks key validity, suspension, tool ACL, rate limits, credit balance, and spending limits. Supports alias keys. Useful for agents that want to pre-flight check a call before committing.
|
|
2275
2277
|
|
|
2278
|
+
### Batch Dry Run
|
|
2279
|
+
|
|
2280
|
+
Simulate multiple tool calls at once to check if an entire batch would succeed:
|
|
2281
|
+
|
|
2282
|
+
```bash
|
|
2283
|
+
curl -X POST http://localhost:3402/requests/dry-run/batch \
|
|
2284
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY" \
|
|
2285
|
+
-d '{"key": "pg_...", "tools": [{"name": "tool_a"}, {"name": "tool_b"}]}'
|
|
2286
|
+
```
|
|
2287
|
+
|
|
2288
|
+
**Response:**
|
|
2289
|
+
|
|
2290
|
+
```json
|
|
2291
|
+
{
|
|
2292
|
+
"allAllowed": true,
|
|
2293
|
+
"totalCreditsRequired": 10,
|
|
2294
|
+
"creditsAvailable": 100,
|
|
2295
|
+
"creditsAfter": 90,
|
|
2296
|
+
"results": [
|
|
2297
|
+
{ "tool": "tool_a", "allowed": true, "creditsRequired": 5 },
|
|
2298
|
+
{ "tool": "tool_b", "allowed": true, "creditsRequired": 5 }
|
|
2299
|
+
]
|
|
2300
|
+
}
|
|
2301
|
+
```
|
|
2302
|
+
|
|
2303
|
+
Performs aggregate credit check (sum of all tool prices vs balance), per-tool ACL validation, spending limit, and rate limit checks. Returns per-tool results so you can see which specific tools would fail. Max 100 tools per batch. Supports alias keys.
|
|
2304
|
+
|
|
2305
|
+
### Tool Availability
|
|
2306
|
+
|
|
2307
|
+
Check per-key tool availability including pricing, affordability, and rate limit status:
|
|
2308
|
+
|
|
2309
|
+
```bash
|
|
2310
|
+
curl "http://localhost:3402/tools/available?key=pg_..." \
|
|
2311
|
+
-H "X-Admin-Key: YOUR_ADMIN_KEY"
|
|
2312
|
+
```
|
|
2313
|
+
|
|
2314
|
+
**Response:**
|
|
2315
|
+
|
|
2316
|
+
```json
|
|
2317
|
+
{
|
|
2318
|
+
"key": "pg_c815...09a6",
|
|
2319
|
+
"creditsAvailable": 100,
|
|
2320
|
+
"totalTools": 3,
|
|
2321
|
+
"accessibleTools": 2,
|
|
2322
|
+
"globalRateLimit": { "limit": 60, "used": 5, "remaining": 55, "resetInMs": 45000 },
|
|
2323
|
+
"tools": [
|
|
2324
|
+
{ "tool": "tool_a", "accessible": true, "creditsPerCall": 10, "canAfford": true },
|
|
2325
|
+
{ "tool": "tool_b", "accessible": false, "denyReason": "denied_by_acl", "creditsPerCall": 5, "canAfford": true },
|
|
2326
|
+
{ "tool": "tool_c", "accessible": true, "creditsPerCall": 1, "canAfford": true, "rateLimit": { "limit": 10, "used": 3, "remaining": 7 } }
|
|
2327
|
+
]
|
|
2328
|
+
}
|
|
2329
|
+
```
|
|
2330
|
+
|
|
2331
|
+
Returns every discovered tool with: `accessible` (ACL check), `denyReason` (if blocked), `creditsPerCall`, `canAfford` (credits vs price), and per-tool `rateLimit` when configured. Includes global rate limit info. Supports alias keys. Works on suspended keys (informational). Read-only — does not deduct credits or increment rate counters.
|
|
2332
|
+
|
|
2276
2333
|
### IP Allowlisting
|
|
2277
2334
|
|
|
2278
2335
|
Restrict API keys to specific IP addresses or CIDR ranges:
|
package/dist/server.d.ts
CHANGED
|
@@ -317,7 +317,9 @@ export declare class PayGateServer {
|
|
|
317
317
|
private handleRequestLog;
|
|
318
318
|
private handleToolStats;
|
|
319
319
|
private handleRequestDryRun;
|
|
320
|
+
private handleRequestDryRunBatch;
|
|
320
321
|
private handleRequestLogExport;
|
|
322
|
+
private handleToolAvailability;
|
|
321
323
|
/** Calculate percentile from an array of numbers. */
|
|
322
324
|
private percentile;
|
|
323
325
|
}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -690,6 +690,12 @@ class PayGateServer {
|
|
|
690
690
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
691
691
|
res.end(JSON.stringify({ error: 'Method not allowed. Use POST.' }));
|
|
692
692
|
return;
|
|
693
|
+
case '/requests/dry-run/batch':
|
|
694
|
+
if (req.method === 'POST')
|
|
695
|
+
return this.handleRequestDryRunBatch(req, res);
|
|
696
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
697
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use POST.' }));
|
|
698
|
+
return;
|
|
693
699
|
// ─── Registry / Discovery endpoints ──────────────────────────────
|
|
694
700
|
case '/.well-known/mcp-payment':
|
|
695
701
|
return this.handlePaymentMetadata(req, res);
|
|
@@ -705,6 +711,12 @@ class PayGateServer {
|
|
|
705
711
|
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
706
712
|
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
707
713
|
return;
|
|
714
|
+
case '/tools/available':
|
|
715
|
+
if (req.method === 'GET')
|
|
716
|
+
return this.handleToolAvailability(req, res);
|
|
717
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
718
|
+
res.end(JSON.stringify({ error: 'Method not allowed. Use GET.' }));
|
|
719
|
+
return;
|
|
708
720
|
case '/alerts':
|
|
709
721
|
if (req.method === 'GET')
|
|
710
722
|
return this.handleGetAlerts(req, res);
|
|
@@ -1316,7 +1328,9 @@ class PayGateServer {
|
|
|
1316
1328
|
requestLog: 'GET /requests — Queryable log of tool call requests with timing, credits, status (requires X-Admin-Key)',
|
|
1317
1329
|
requestLogExport: 'GET /requests/export — Export request log as JSON or CSV with filters (requires X-Admin-Key)',
|
|
1318
1330
|
requestDryRun: 'POST /requests/dry-run — Simulate a tool call without executing (requires X-Admin-Key)',
|
|
1331
|
+
requestDryRunBatch: 'POST /requests/dry-run/batch — Simulate multiple tool calls without executing (requires X-Admin-Key)',
|
|
1319
1332
|
toolStats: 'GET /tools/stats — Per-tool call counts, success rates, latency, credits, and top consumers (requires X-Admin-Key)',
|
|
1333
|
+
toolAvailability: 'GET /tools/available?key=... — Per-key tool availability with pricing, affordability, and rate limit status (requires X-Admin-Key)',
|
|
1320
1334
|
...(this.oauth ? {
|
|
1321
1335
|
oauthMetadata: 'GET /.well-known/oauth-authorization-server — OAuth 2.1 server metadata',
|
|
1322
1336
|
oauthRegister: 'POST /oauth/register — Register OAuth client',
|
|
@@ -6591,6 +6605,142 @@ class PayGateServer {
|
|
|
6591
6605
|
res.end(JSON.stringify({ error: 'Failed to read request body' }));
|
|
6592
6606
|
}
|
|
6593
6607
|
}
|
|
6608
|
+
// ─── /requests/dry-run/batch — Simulate multiple tool calls without executing ──
|
|
6609
|
+
async handleRequestDryRunBatch(req, res) {
|
|
6610
|
+
if (!this.checkAdmin(req, res))
|
|
6611
|
+
return;
|
|
6612
|
+
try {
|
|
6613
|
+
const raw = await this.readBody(req);
|
|
6614
|
+
try {
|
|
6615
|
+
const params = JSON.parse(raw);
|
|
6616
|
+
const apiKey = params.key;
|
|
6617
|
+
const tools = params.tools;
|
|
6618
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
6619
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6620
|
+
res.end(JSON.stringify({ error: 'Missing required field: key' }));
|
|
6621
|
+
return;
|
|
6622
|
+
}
|
|
6623
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
6624
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6625
|
+
res.end(JSON.stringify({ error: 'Missing required field: tools (non-empty array of {name} objects)' }));
|
|
6626
|
+
return;
|
|
6627
|
+
}
|
|
6628
|
+
if (tools.length > 100) {
|
|
6629
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6630
|
+
res.end(JSON.stringify({ error: 'Maximum 100 tools per batch dry run' }));
|
|
6631
|
+
return;
|
|
6632
|
+
}
|
|
6633
|
+
// Validate tool entries
|
|
6634
|
+
for (let i = 0; i < tools.length; i++) {
|
|
6635
|
+
if (!tools[i]?.name || typeof tools[i].name !== 'string') {
|
|
6636
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6637
|
+
res.end(JSON.stringify({ error: `tools[${i}] missing required "name" field` }));
|
|
6638
|
+
return;
|
|
6639
|
+
}
|
|
6640
|
+
}
|
|
6641
|
+
// Step 1: Key lookup (resolveKeyRaw handles aliases)
|
|
6642
|
+
const keyRecord = this.gate.store.resolveKeyRaw(apiKey);
|
|
6643
|
+
if (!keyRecord) {
|
|
6644
|
+
const isExpired = this.gate.store.isExpired(apiKey);
|
|
6645
|
+
const reason = isExpired ? 'api_key_expired' : 'invalid_api_key';
|
|
6646
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6647
|
+
res.end(JSON.stringify({
|
|
6648
|
+
allAllowed: false,
|
|
6649
|
+
reason,
|
|
6650
|
+
totalCreditsRequired: 0,
|
|
6651
|
+
results: tools.map((t) => ({ tool: t.name, allowed: false, reason, creditsRequired: 0 })),
|
|
6652
|
+
}));
|
|
6653
|
+
return;
|
|
6654
|
+
}
|
|
6655
|
+
// Step 2: Suspended?
|
|
6656
|
+
if (keyRecord.suspended) {
|
|
6657
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6658
|
+
res.end(JSON.stringify({
|
|
6659
|
+
allAllowed: false,
|
|
6660
|
+
reason: 'key_suspended',
|
|
6661
|
+
totalCreditsRequired: 0,
|
|
6662
|
+
creditsAvailable: keyRecord.credits,
|
|
6663
|
+
results: tools.map((t) => ({ tool: t.name, allowed: false, reason: 'key_suspended', creditsRequired: 0 })),
|
|
6664
|
+
}));
|
|
6665
|
+
return;
|
|
6666
|
+
}
|
|
6667
|
+
// Step 3: Rate limit check (read-only)
|
|
6668
|
+
const rateStatus = this.gate.rateLimiter.getStatus(keyRecord.key);
|
|
6669
|
+
if (rateStatus.limit > 0 && rateStatus.remaining <= 0) {
|
|
6670
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6671
|
+
res.end(JSON.stringify({
|
|
6672
|
+
allAllowed: false,
|
|
6673
|
+
reason: 'rate_limited',
|
|
6674
|
+
totalCreditsRequired: 0,
|
|
6675
|
+
creditsAvailable: keyRecord.credits,
|
|
6676
|
+
rateLimit: rateStatus,
|
|
6677
|
+
results: tools.map((t) => ({ tool: t.name, allowed: false, reason: 'rate_limited', creditsRequired: 0 })),
|
|
6678
|
+
}));
|
|
6679
|
+
return;
|
|
6680
|
+
}
|
|
6681
|
+
// Step 4: Per-tool checks
|
|
6682
|
+
const results = [];
|
|
6683
|
+
let totalCreditsRequired = 0;
|
|
6684
|
+
let allAllowed = true;
|
|
6685
|
+
let firstDenyReason;
|
|
6686
|
+
for (const toolEntry of tools) {
|
|
6687
|
+
const toolName = toolEntry.name;
|
|
6688
|
+
const toolArgs = toolEntry.arguments;
|
|
6689
|
+
// ACL check
|
|
6690
|
+
const effectiveAllowed = keyRecord.allowedTools || [];
|
|
6691
|
+
const effectiveDenied = keyRecord.deniedTools || [];
|
|
6692
|
+
if (effectiveDenied.includes(toolName)) {
|
|
6693
|
+
results.push({ tool: toolName, allowed: false, reason: `tool_not_allowed: ${toolName} is in deniedTools`, creditsRequired: 0 });
|
|
6694
|
+
allAllowed = false;
|
|
6695
|
+
if (!firstDenyReason)
|
|
6696
|
+
firstDenyReason = `tool_not_allowed: ${toolName}`;
|
|
6697
|
+
continue;
|
|
6698
|
+
}
|
|
6699
|
+
if (effectiveAllowed.length > 0 && !effectiveAllowed.includes(toolName)) {
|
|
6700
|
+
results.push({ tool: toolName, allowed: false, reason: `tool_not_allowed: ${toolName} not in allowedTools`, creditsRequired: 0 });
|
|
6701
|
+
allAllowed = false;
|
|
6702
|
+
if (!firstDenyReason)
|
|
6703
|
+
firstDenyReason = `tool_not_allowed: ${toolName}`;
|
|
6704
|
+
continue;
|
|
6705
|
+
}
|
|
6706
|
+
const creditsRequired = this.gate.getToolPrice(toolName, toolArgs);
|
|
6707
|
+
totalCreditsRequired += creditsRequired;
|
|
6708
|
+
results.push({ tool: toolName, allowed: true, creditsRequired });
|
|
6709
|
+
}
|
|
6710
|
+
// Step 5: Aggregate credits check
|
|
6711
|
+
if (allAllowed && keyRecord.credits < totalCreditsRequired) {
|
|
6712
|
+
allAllowed = false;
|
|
6713
|
+
firstDenyReason = `insufficient_credits: need ${totalCreditsRequired}, have ${keyRecord.credits}`;
|
|
6714
|
+
}
|
|
6715
|
+
// Step 6: Spending limit
|
|
6716
|
+
if (allAllowed && keyRecord.spendingLimit > 0) {
|
|
6717
|
+
const wouldSpend = keyRecord.totalSpent + totalCreditsRequired;
|
|
6718
|
+
if (wouldSpend > keyRecord.spendingLimit) {
|
|
6719
|
+
allAllowed = false;
|
|
6720
|
+
firstDenyReason = `spending_limit_exceeded: limit ${keyRecord.spendingLimit}, spent ${keyRecord.totalSpent}, need ${totalCreditsRequired}`;
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6723
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6724
|
+
res.end(JSON.stringify({
|
|
6725
|
+
allAllowed,
|
|
6726
|
+
...(firstDenyReason ? { reason: firstDenyReason } : {}),
|
|
6727
|
+
totalCreditsRequired,
|
|
6728
|
+
creditsAvailable: keyRecord.credits,
|
|
6729
|
+
...(allAllowed ? { creditsAfter: keyRecord.credits - totalCreditsRequired } : {}),
|
|
6730
|
+
...(rateStatus.limit > 0 ? { rateLimit: rateStatus } : {}),
|
|
6731
|
+
results,
|
|
6732
|
+
}));
|
|
6733
|
+
}
|
|
6734
|
+
catch {
|
|
6735
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6736
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
catch {
|
|
6740
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6741
|
+
res.end(JSON.stringify({ error: 'Failed to read request body' }));
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6594
6744
|
// ─── /requests/export — Export request log as JSON or CSV ───────────────────
|
|
6595
6745
|
handleRequestLogExport(req, res) {
|
|
6596
6746
|
if (!this.checkAdmin(req, res))
|
|
@@ -6672,6 +6822,73 @@ class PayGateServer {
|
|
|
6672
6822
|
res.end(JSON.stringify({ count: sorted.length, requests: sorted }, null, 2));
|
|
6673
6823
|
}
|
|
6674
6824
|
}
|
|
6825
|
+
// ─── /tools/available — Per-key tool availability with pricing ──────────────
|
|
6826
|
+
handleToolAvailability(req, res) {
|
|
6827
|
+
if (!this.checkAdmin(req, res))
|
|
6828
|
+
return;
|
|
6829
|
+
const urlParts = req.url?.split('?') || [];
|
|
6830
|
+
const params = new URLSearchParams(urlParts[1] || '');
|
|
6831
|
+
const keyParam = params.get('key');
|
|
6832
|
+
if (!keyParam) {
|
|
6833
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
6834
|
+
res.end(JSON.stringify({ error: 'Missing required parameter: key' }));
|
|
6835
|
+
return;
|
|
6836
|
+
}
|
|
6837
|
+
// Resolve alias
|
|
6838
|
+
const keyRecord = this.gate.store.resolveKeyRaw(keyParam);
|
|
6839
|
+
if (!keyRecord) {
|
|
6840
|
+
const isExpired = this.gate.store.isExpired(keyParam);
|
|
6841
|
+
const reason = isExpired ? 'api_key_expired' : 'invalid_api_key';
|
|
6842
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6843
|
+
res.end(JSON.stringify({ error: reason, tools: [] }));
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
6846
|
+
// Get all discovered tools with pricing
|
|
6847
|
+
const allToolPricing = this.registry.getFullPricing().tools;
|
|
6848
|
+
// Build per-tool availability
|
|
6849
|
+
const effectiveAllowed = keyRecord.allowedTools || [];
|
|
6850
|
+
const effectiveDenied = keyRecord.deniedTools || [];
|
|
6851
|
+
const tools = allToolPricing.map(toolInfo => {
|
|
6852
|
+
const toolName = toolInfo.name;
|
|
6853
|
+
// ACL check
|
|
6854
|
+
let accessible = true;
|
|
6855
|
+
let denyReason;
|
|
6856
|
+
if (effectiveDenied.includes(toolName)) {
|
|
6857
|
+
accessible = false;
|
|
6858
|
+
denyReason = 'denied_by_acl';
|
|
6859
|
+
}
|
|
6860
|
+
else if (effectiveAllowed.length > 0 && !effectiveAllowed.includes(toolName)) {
|
|
6861
|
+
accessible = false;
|
|
6862
|
+
denyReason = 'not_in_allowed_list';
|
|
6863
|
+
}
|
|
6864
|
+
// Affordability
|
|
6865
|
+
const creditsRequired = toolInfo.creditsPerCall;
|
|
6866
|
+
const canAfford = keyRecord.credits >= creditsRequired;
|
|
6867
|
+
// Per-tool rate limit status
|
|
6868
|
+
const perToolRateLimit = toolInfo.rateLimitPerMin > 0
|
|
6869
|
+
? this.gate.rateLimiter.getStatus(`${keyRecord.key}:tool:${toolName}`, toolInfo.rateLimitPerMin)
|
|
6870
|
+
: null;
|
|
6871
|
+
return {
|
|
6872
|
+
tool: toolName,
|
|
6873
|
+
accessible,
|
|
6874
|
+
...(denyReason ? { denyReason } : {}),
|
|
6875
|
+
creditsPerCall: creditsRequired,
|
|
6876
|
+
canAfford,
|
|
6877
|
+
...(perToolRateLimit ? { rateLimit: perToolRateLimit } : {}),
|
|
6878
|
+
};
|
|
6879
|
+
});
|
|
6880
|
+
// Global rate limit
|
|
6881
|
+
const globalRateLimit = this.gate.rateLimiter.getStatus(keyRecord.key);
|
|
6882
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
6883
|
+
res.end(JSON.stringify({
|
|
6884
|
+
key: keyRecord.key.slice(0, 7) + '...' + keyRecord.key.slice(-4),
|
|
6885
|
+
creditsAvailable: keyRecord.credits,
|
|
6886
|
+
totalTools: tools.length,
|
|
6887
|
+
accessibleTools: tools.filter(t => t.accessible).length,
|
|
6888
|
+
...(globalRateLimit.limit > 0 ? { globalRateLimit } : {}),
|
|
6889
|
+
tools,
|
|
6890
|
+
}));
|
|
6891
|
+
}
|
|
6675
6892
|
/** Calculate percentile from an array of numbers. */
|
|
6676
6893
|
percentile(values, p) {
|
|
6677
6894
|
if (values.length === 0)
|