0nmcp 2.3.0 → 2.4.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 +44 -29
- package/capability-proxy.js +199 -0
- package/catalog.js +322 -6
- package/crm/helpers.js +9 -6
- package/crm/index.js +3 -2
- package/engine/validator.js +13 -1
- package/index.js +8 -5
- package/lib/stats.json +1 -1
- package/orchestrator.js +6 -35
- package/package.json +3 -2
- package/tools.js +3 -36
- package/workflow.js +336 -12
package/README.md
CHANGED
|
@@ -19,21 +19,21 @@
|
|
|
19
19
|
[](https://nodejs.org)
|
|
20
20
|
[](https://modelcontextprotocol.io)
|
|
21
21
|
[](http://makeapullrequest.com)
|
|
22
|
-
[](#-supported-services)
|
|
23
|
+
[](#-all-tools)
|
|
24
24
|
[](#-community)
|
|
25
25
|
[](https://0nmcp.com)
|
|
26
26
|
[](https://github.com/0nork/0nMCP/discussions)
|
|
27
27
|
|
|
28
|
-
**
|
|
28
|
+
**850 tools. 53 services. Zero configuration. One natural language interface.**
|
|
29
29
|
|
|
30
|
-
[Website](https://0nmcp.com) · [Quick Start](#-installation) · [Services](#-supported-services) · [
|
|
30
|
+
[Website](https://0nmcp.com) · [Quick Start](#-installation) · [Services](#-supported-services) · [850 Tools](#-all-tools) · [.0n Standard](#-the-0n-standard) · [Unlocks](#-unlocks) · [Community](https://0nmcp.com/community)
|
|
31
31
|
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
> **v2.
|
|
36
|
+
> **v2.3.0** — 850 tools across 53 services in 23 categories. 1,142 total capabilities. New in v2.3: **5 new services** (Cloudflare, GoDaddy, n8n, Pabbly, Make), **Resend expanded** (3→67 endpoints), **ACTION_ALIASES conversion layer** for intuitive .0n workflow authoring, **connection auto-enrichment** (locationId, pipelineId injected from .0n files), and **enhanced API validation** (CRM, Anthropic, Vercel). [See what's new](#-whats-new-in-v23).
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -180,11 +180,16 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) o
|
|
|
180
180
|
| **Microsoft Azure** | Cloud | Resources, resource groups, storage, VMs via ARM |
|
|
181
181
|
| **Pipedrive** | CRM | Deals, persons, organizations, activities, pipelines, notes |
|
|
182
182
|
| **LinkedIn** | Social | Posts, profile, connections, organization pages |
|
|
183
|
+
| **Cloudflare** | Cloud | DNS zones, records, workers, KV storage, page rules, firewall |
|
|
184
|
+
| **GoDaddy** | Cloud | Domains, DNS, availability, agreements, renewals |
|
|
185
|
+
| **n8n** | Automation | Workflows, executions, credentials — self-hosted automation |
|
|
186
|
+
| **Pabbly** | Automation | Workflows, triggers, actions, connections |
|
|
187
|
+
| **Make** | Automation | Scenarios, organizations, data stores, connections |
|
|
183
188
|
| **CRM** | CRM | **245 tools** — contacts, conversations, calendars, invoices, payments, products, pipelines, social media, custom objects, and more |
|
|
184
189
|
|
|
185
|
-
**
|
|
190
|
+
**53 services. 850 tools. 23 categories. One interface.**
|
|
186
191
|
|
|
187
|
-
> **More coming:** AWS S3, Vercel,
|
|
192
|
+
> **More coming:** AWS S3, Vercel, Firebase, Figma, DocuSign, Twilio Flex...
|
|
188
193
|
|
|
189
194
|
---
|
|
190
195
|
|
|
@@ -269,7 +274,7 @@ One tool call. Everything deployed:
|
|
|
269
274
|
| `connect_service` | Connect a new service with credentials |
|
|
270
275
|
| `disconnect_service` | Remove a connected service |
|
|
271
276
|
| `list_connections` | See what's connected and capability counts |
|
|
272
|
-
| `list_available_services` | Browse all
|
|
277
|
+
| `list_available_services` | Browse all 53 services grouped by category |
|
|
273
278
|
| `get_service_info` | Deep dive on a specific service — endpoints, auth, capabilities |
|
|
274
279
|
| `api_call` | Direct API call to any connected service endpoint |
|
|
275
280
|
|
|
@@ -292,17 +297,27 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
|
|
|
292
297
|
| **Users** | 24 | Users, forms, surveys, funnels, media, companies, businesses |
|
|
293
298
|
| **Objects** | 34 | Custom objects, associations, email, workflows, snapshots, links, campaigns, courses, SaaS |
|
|
294
299
|
|
|
295
|
-
**
|
|
300
|
+
**850 total tools.** Universal orchestration (576 catalog tools across 53 services) + the most comprehensive CRM integration in the MCP ecosystem (245 dedicated tools) + Vault (4 tools) + Vault Containers (8 tools) + Business Deed Transfer (6 tools) + Engine (6 tools) + App Builder (5 tools).
|
|
296
301
|
|
|
297
302
|
> Every CRM tool is data-driven — defined as configuration, not code. Adding new endpoints takes minutes, not hours. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
298
303
|
|
|
299
304
|
---
|
|
300
305
|
|
|
301
|
-
## What's New in v2.
|
|
306
|
+
## What's New in v2.3
|
|
307
|
+
|
|
308
|
+
### v2.3.0 — Conversion Layer + 5 New Services
|
|
309
|
+
|
|
310
|
+
- **5 new services**: Cloudflare (DNS, Workers, KV), GoDaddy (domains), n8n (self-hosted automation), Pabbly (workflows), Make (scenarios)
|
|
311
|
+
- **68 new catalog endpoints** — from 508 to 576 catalog tools
|
|
312
|
+
- **Resend expanded**: 3→67 endpoints — full Resend API coverage (domains, contacts, audiences, broadcasts)
|
|
313
|
+
- **ACTION_ALIASES conversion layer** — 150+ intuitive action mappings (e.g., `contacts.create` → `create_contact`) so .0n SWITCH files use natural action names
|
|
314
|
+
- **Connection auto-enrichment** — workflow runner automatically injects `locationId`, `pipelineId`, `projectRef` from .0n connection metadata
|
|
315
|
+
- **Enhanced API validation** — CRM, Anthropic, and Vercel verification endpoints added to engine validator
|
|
316
|
+
- Total: **850 tools across 53 services in 23 categories**
|
|
302
317
|
|
|
303
318
|
### v2.2.0 — 22 New Services Expansion
|
|
304
319
|
|
|
305
|
-
- **255 new catalog endpoints** across 22 new services — from 290 to
|
|
320
|
+
- **255 new catalog endpoints** across 22 new services — from 290 to 576 catalog tools
|
|
306
321
|
- **6 new categories**: Accounting, Advertising, Finance, Cloud, Integration, Automation
|
|
307
322
|
- **Advertising suite**: Google Ads, Facebook Ads, TikTok Ads, X Ads, LinkedIn Ads, Instagram Ads
|
|
308
323
|
- **Social expansion**: Instagram, X (Twitter), TikTok Business, LinkedIn
|
|
@@ -310,7 +325,7 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
|
|
|
310
325
|
- **Infrastructure**: Dropbox, MuleSoft, Microsoft Azure, Zapier
|
|
311
326
|
- **Communication**: WhatsApp Business
|
|
312
327
|
- **Marketing**: Smartlead cold email outreach
|
|
313
|
-
- Total: **
|
|
328
|
+
- Total: **850 tools across 53 services in 23 categories**
|
|
314
329
|
|
|
315
330
|
### v2.1.0 — Business Deed Transfer System
|
|
316
331
|
|
|
@@ -333,13 +348,13 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
|
|
|
333
348
|
|
|
334
349
|
### v1.7.0 — Foundation
|
|
335
350
|
|
|
336
|
-
- 550 tools across
|
|
337
|
-
- **.0n Conversion Engine** — import credentials, auto-map to
|
|
351
|
+
- 550 tools across 53 services in 13 categories
|
|
352
|
+
- **.0n Conversion Engine** — import credentials, auto-map to 53 services, generate configs for 7 AI platforms
|
|
338
353
|
- **Vault** — machine-bound encrypted credential storage (AES-256-GCM + PBKDF2-SHA512 + hardware fingerprint)
|
|
339
354
|
- **Workflow Runtime** + **HTTP Server** + **CLI with named runs**
|
|
340
355
|
- **Three-Level Execution** (Patent Pending) — Pipeline → Assembly Line → Radial Burst
|
|
341
356
|
|
|
342
|
-
> **
|
|
357
|
+
> **850 tools. 53 services. 1,142 total capabilities.** See [CHANGELOG.md](CHANGELOG.md) for full version history and the [What's New in v2.3](#-whats-new-in-v23) section above.
|
|
343
358
|
|
|
344
359
|
---
|
|
345
360
|
|
|
@@ -353,7 +368,7 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
|
|
|
353
368
|
│ for $500 and │ │ 2. Plan steps │ │ SendGrid │
|
|
354
369
|
│ notify #sales" │ │ 3. Execute APIs │ │ Slack │
|
|
355
370
|
│ │◀────│ 4. Chain data │◀────│ CRM │
|
|
356
|
-
│ │ │ 5. Summarize │ │ +
|
|
371
|
+
│ │ │ 5. Summarize │ │ + 49 more... │
|
|
357
372
|
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
358
373
|
```
|
|
359
374
|
|
|
@@ -378,7 +393,7 @@ The orchestrator uses keyword matching to route tasks to the right service. Less
|
|
|
378
393
|
| **Flexibility** | Say what you want | Triggers/actions only | Unlimited but complex |
|
|
379
394
|
| **Maintenance** | Zero | Update broken zaps | Fix API changes |
|
|
380
395
|
| **Open source** | Yes (MIT) | No | Depends |
|
|
381
|
-
| **Tools available** |
|
|
396
|
+
| **Tools available** | 850 | Varies | Whatever you build |
|
|
382
397
|
|
|
383
398
|
---
|
|
384
399
|
|
|
@@ -430,7 +445,7 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
|
|
|
430
445
|
```
|
|
431
446
|
0nMCP/
|
|
432
447
|
├── index.js # Entry point — MCP server startup
|
|
433
|
-
├── catalog.js # Service catalog —
|
|
448
|
+
├── catalog.js # Service catalog — 53 integrations with endpoints
|
|
434
449
|
├── connections.js # Connection manager — ~/.0n/connections/*.0n
|
|
435
450
|
├── orchestrator.js # AI execution planner — the brain
|
|
436
451
|
├── workflow.js # WorkflowRunner — .0n file execution
|
|
@@ -471,7 +486,7 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
|
|
|
471
486
|
├── engine/ # .0n Conversion Engine + App Builder
|
|
472
487
|
│ ├── index.js # Engine entry — 6 tools
|
|
473
488
|
│ ├── parser.js # Multi-format credential parser
|
|
474
|
-
│ ├── mapper.js # Auto-map credentials to
|
|
489
|
+
│ ├── mapper.js # Auto-map credentials to 53 services
|
|
475
490
|
│ ├── validator.js # API key verification
|
|
476
491
|
│ ├── platforms.js # 7 AI platform config generators
|
|
477
492
|
│ ├── bundler.js # Portable .0n bundle creator
|
|
@@ -490,14 +505,14 @@ Every task execution is logged to `~/.0n/history/` as JSONL — full audit trail
|
|
|
490
505
|
|
|
491
506
|
| Component | What It Does |
|
|
492
507
|
|-----------|-------------|
|
|
493
|
-
| **Service Catalog** | Defines all
|
|
508
|
+
| **Service Catalog** | Defines all 53 services — their base URLs, endpoints, auth patterns, and capabilities |
|
|
494
509
|
| **Connection Manager** | Stores credentials as `.0n` files in `~/.0n/connections/` per the .0n standard |
|
|
495
510
|
| **Orchestrator** | The brain — parses natural language, plans multi-step execution, calls APIs, chains data |
|
|
496
511
|
| **CRM Modules** | 245 tools across 12 modules — data-driven, every tool is config not code |
|
|
497
512
|
| **Vault** | Machine-bound encrypted credential storage — AES-256-GCM + PBKDF2-SHA512 |
|
|
498
513
|
| **Vault Container System** | Patent Pending #63/990,046 — 7 semantic layers, multi-party escrow, Seal of Truth, binary .0nv format |
|
|
499
514
|
| **Business Deed Transfer** | Package + escrow + transfer entire digital businesses in encrypted containers |
|
|
500
|
-
| **Conversion Engine** | Import credentials from .env/CSV/JSON, auto-map to
|
|
515
|
+
| **Conversion Engine** | Import credentials from .env/CSV/JSON, auto-map to 53 services, generate 7 AI platform configs |
|
|
501
516
|
| **Application Engine** | Build, distribute, schedule .0n applications with CronScheduler + HTTP middleware |
|
|
502
517
|
| **Workflow Runtime** | Load and execute `.0n` workflow files with template engine, conditions, and step chaining |
|
|
503
518
|
| **HTTP Server** | Express-based REST API, MCP over HTTP, and webhook receivers |
|
|
@@ -607,7 +622,7 @@ The tool factory handles registration, validation, API calls, error handling —
|
|
|
607
622
|
|
|
608
623
|
### Phase 0 — Foundation (Current)
|
|
609
624
|
|
|
610
|
-
- [x] **
|
|
625
|
+
- [x] **53 services, 850 tools, 23 categories**
|
|
611
626
|
- [x] Core orchestration engine with AI planning
|
|
612
627
|
- [x] **245 CRM tools** — full API coverage across 12 modules
|
|
613
628
|
- [x] Gmail, Google Sheets, Google Drive, Jira, Zendesk, Mailchimp, Zoom, Microsoft 365, MongoDB
|
|
@@ -625,7 +640,7 @@ The tool factory handles registration, validation, API calls, error handling —
|
|
|
625
640
|
- [x] **Asana** — project and task management
|
|
626
641
|
- [x] **Intercom** — customer messaging
|
|
627
642
|
- [x] **22 new services** — advertising, social, finance, cloud, automation
|
|
628
|
-
- [x] Target:
|
|
643
|
+
- [x] Target: 53 services, 850 tools (exceeded)
|
|
629
644
|
|
|
630
645
|
### Phase 2 — Full Stack (500 stars / $2K MRR)
|
|
631
646
|
|
|
@@ -691,9 +706,9 @@ We ship weekly. The codebase is active. The community is real. If you're buildin
|
|
|
691
706
|
|
|
692
707
|
| Metric | |
|
|
693
708
|
|--------|---|
|
|
694
|
-
| **Tools shipped** |
|
|
695
|
-
| **Services integrated** |
|
|
696
|
-
| **Categories** |
|
|
709
|
+
| **Tools shipped** | 850 |
|
|
710
|
+
| **Services integrated** | 53 |
|
|
711
|
+
| **Categories** | 23 |
|
|
697
712
|
| **CRM endpoints covered** | 245 / 245 (100%) |
|
|
698
713
|
| **npm packages** | 3 ([0nmcp](https://www.npmjs.com/package/0nmcp), [0nork](https://www.npmjs.com/package/0nork), [0n-spec](https://www.npmjs.com/package/0n-spec)) |
|
|
699
714
|
| **Open source repos** | 3 |
|
|
@@ -742,7 +757,7 @@ node index.js
|
|
|
742
757
|
|
|
743
758
|
| Project | Description |
|
|
744
759
|
|---------|-------------|
|
|
745
|
-
| **[0nMCP](https://0nmcp.com)** | Universal AI API Orchestrator —
|
|
760
|
+
| **[0nMCP](https://0nmcp.com)** | Universal AI API Orchestrator — 850 tools, 53 services, Vault encryption, Business Deed transfer ([source](https://github.com/0nork/0nMCP)) |
|
|
746
761
|
| **[0n-spec](https://github.com/0nork/0n-spec)** | The .0n Standard — universal configuration format for AI orchestration |
|
|
747
762
|
| **[0nork](https://github.com/0nork/0nork)** | The parent org — AI orchestration infrastructure |
|
|
748
763
|
|
|
@@ -757,7 +772,7 @@ node index.js
|
|
|
757
772
|
|
|
758
773
|
<div align="center">
|
|
759
774
|
|
|
760
|
-
**[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%
|
|
775
|
+
**[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%20850%20tools,%2053%20services,%20zero%20config.%20The%20universal%20AI%20API%20orchestrator.%20Free%20and%20open%20source.&url=https://github.com/0nork/0nMCP)**
|
|
761
776
|
|
|
762
777
|
</div>
|
|
763
778
|
|
|
@@ -767,7 +782,7 @@ node index.js
|
|
|
767
782
|
|
|
768
783
|
### Stop building workflows. Start describing outcomes.
|
|
769
784
|
|
|
770
|
-
**
|
|
785
|
+
**850 tools. 53 services. Zero config. MIT licensed. Community driven.**
|
|
771
786
|
|
|
772
787
|
**[Get Started](https://0nmcp.com)** · **[Join the Community](https://0nmcp.com/community)** · **[Unlock Schedule](https://0nmcp.com/sponsor)** · **[View Source](https://github.com/0nork/0nMCP)** · **[Read the Spec](https://github.com/0nork/0n-spec)**
|
|
773
788
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — Capability Proxy
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Zero-knowledge API execution layer. Tools call capabilities,
|
|
5
|
+
// not accounts. Credentials are borrowed from vault/connections,
|
|
6
|
+
// held in memory only for the duration of the call, then wiped.
|
|
7
|
+
//
|
|
8
|
+
// Wires in rate limiting (ratelimit.js) and audit logging.
|
|
9
|
+
// Credentials NEVER appear in logs, errors, or tool responses.
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
import { appendFileSync, existsSync, mkdirSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { getLimiter, retryWithBackoff } from "./ratelimit.js";
|
|
16
|
+
|
|
17
|
+
const AUDIT_DIR = join(homedir(), ".0n", "history");
|
|
18
|
+
const AUDIT_FILE = join(AUDIT_DIR, "audit.jsonl");
|
|
19
|
+
|
|
20
|
+
export class CapabilityProxy {
|
|
21
|
+
/**
|
|
22
|
+
* @param {import("./connections.js").ConnectionManager} connectionManager
|
|
23
|
+
* @param {Record<string, any>} catalog - SERVICE_CATALOG from catalog.js
|
|
24
|
+
*/
|
|
25
|
+
constructor(connectionManager, catalog) {
|
|
26
|
+
this.connections = connectionManager;
|
|
27
|
+
this.catalog = catalog;
|
|
28
|
+
|
|
29
|
+
// Ensure audit directory exists
|
|
30
|
+
if (!existsSync(AUDIT_DIR)) {
|
|
31
|
+
mkdirSync(AUDIT_DIR, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Primary method — tools call this instead of fetch() ──────
|
|
36
|
+
/**
|
|
37
|
+
* Execute an API call through the proxy.
|
|
38
|
+
* Borrows credentials, enforces rate limits, executes, wipes credentials, logs audit.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} service - Service key (e.g., "stripe", "sendgrid")
|
|
41
|
+
* @param {string} endpoint - Endpoint name from service catalog
|
|
42
|
+
* @param {Object} params - Request parameters
|
|
43
|
+
* @returns {Promise<{response: Response, data: any}>}
|
|
44
|
+
*/
|
|
45
|
+
async call(service, endpoint, params = {}) {
|
|
46
|
+
const start = Date.now();
|
|
47
|
+
let creds = null;
|
|
48
|
+
let status = 0;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// 1. Borrow credentials (in-memory only)
|
|
52
|
+
creds = this._borrowCredentials(service);
|
|
53
|
+
if (!creds) {
|
|
54
|
+
throw new Error(`Service ${service} not connected`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Resolve catalog entry
|
|
58
|
+
const svc = this.catalog[service];
|
|
59
|
+
if (!svc) throw new Error(`Unknown service: ${service}`);
|
|
60
|
+
|
|
61
|
+
const ep = svc.endpoints?.[endpoint];
|
|
62
|
+
if (!ep) throw new Error(`Unknown endpoint: ${service}.${endpoint}`);
|
|
63
|
+
|
|
64
|
+
// 3. Build request from catalog
|
|
65
|
+
const { url, method, headers, body } = this._buildRequest(svc, ep, params, creds);
|
|
66
|
+
|
|
67
|
+
// 4. Enforce rate limit
|
|
68
|
+
await this._enforceRateLimit(service);
|
|
69
|
+
|
|
70
|
+
// 5. Execute with retry on 429/5xx
|
|
71
|
+
const response = await retryWithBackoff(
|
|
72
|
+
() => fetch(url, { method, headers, body }),
|
|
73
|
+
{ maxRetries: 2, initialDelayMs: 500 }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
status = response.status;
|
|
77
|
+
const data = await response.json().catch(() => ({
|
|
78
|
+
status: response.status,
|
|
79
|
+
statusText: response.statusText,
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
return { response, data };
|
|
83
|
+
} finally {
|
|
84
|
+
// 6. Wipe credentials from local scope
|
|
85
|
+
creds = null;
|
|
86
|
+
|
|
87
|
+
// 7. Log audit entry (NO credentials)
|
|
88
|
+
this._logAudit(service, endpoint, status, Date.now() - start);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── CRM variant — credentials provided per-call ──────────────
|
|
93
|
+
/**
|
|
94
|
+
* Execute with externally-provided credentials (CRM pattern).
|
|
95
|
+
* Used when access_token comes from the MCP tool argument, not ConnectionManager.
|
|
96
|
+
* Still provides rate limiting and audit logging.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} service - Service key (typically "crm")
|
|
99
|
+
* @param {string} endpoint - Endpoint/tool name
|
|
100
|
+
* @param {string} url - Pre-built URL
|
|
101
|
+
* @param {Object} options - Fetch options (method, headers, body)
|
|
102
|
+
* @returns {Promise<{response: Response, data: any}>}
|
|
103
|
+
*/
|
|
104
|
+
async callWithCredentials(service, endpoint, url, options) {
|
|
105
|
+
const start = Date.now();
|
|
106
|
+
let status = 0;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// Enforce rate limit
|
|
110
|
+
await this._enforceRateLimit(service);
|
|
111
|
+
|
|
112
|
+
// Execute with retry
|
|
113
|
+
const response = await retryWithBackoff(
|
|
114
|
+
() => fetch(url, options),
|
|
115
|
+
{ maxRetries: 2, initialDelayMs: 500 }
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
status = response.status;
|
|
119
|
+
const data = await response.json().catch(() => ({
|
|
120
|
+
status: response.status,
|
|
121
|
+
statusText: response.statusText,
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
return { response, data };
|
|
125
|
+
} finally {
|
|
126
|
+
this._logAudit(service, endpoint, status, Date.now() - start);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Internal: borrow credentials ─────────────────────────────
|
|
131
|
+
_borrowCredentials(service) {
|
|
132
|
+
const creds = this.connections.getCredentials(service);
|
|
133
|
+
// Return a shallow copy; original ref released after call
|
|
134
|
+
return creds ? { ...creds } : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Internal: enforce rate limit ─────────────────────────────
|
|
138
|
+
async _enforceRateLimit(service) {
|
|
139
|
+
const limiter = getLimiter(service);
|
|
140
|
+
await limiter.acquire();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Internal: build request from catalog entry ───────────────
|
|
144
|
+
_buildRequest(svc, ep, params, creds) {
|
|
145
|
+
// URL with path param substitution
|
|
146
|
+
const allParams = { ...creds, ...params };
|
|
147
|
+
let url = svc.baseUrl + ep.path;
|
|
148
|
+
url = url.replace(/\{(\w+)\}/g, (_, key) => allParams[key] || `{${key}}`);
|
|
149
|
+
|
|
150
|
+
// Headers from catalog auth
|
|
151
|
+
const headers = svc.authHeader(creds);
|
|
152
|
+
const method = ep.method;
|
|
153
|
+
let body = undefined;
|
|
154
|
+
|
|
155
|
+
// Body for non-GET
|
|
156
|
+
if (method !== "GET" && params && Object.keys(params).length > 0) {
|
|
157
|
+
const contentType = ep.contentType || "application/json";
|
|
158
|
+
if (contentType === "application/x-www-form-urlencoded") {
|
|
159
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
160
|
+
const flat = {};
|
|
161
|
+
for (const [k, v] of Object.entries(params)) {
|
|
162
|
+
if (typeof v !== "object") flat[k] = String(v);
|
|
163
|
+
}
|
|
164
|
+
body = new URLSearchParams(flat).toString();
|
|
165
|
+
} else {
|
|
166
|
+
headers["Content-Type"] = "application/json";
|
|
167
|
+
body = JSON.stringify(params);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Query string for GET
|
|
172
|
+
if (method === "GET" && params && Object.keys(params).length > 0) {
|
|
173
|
+
const flat = {};
|
|
174
|
+
for (const [k, v] of Object.entries(params)) {
|
|
175
|
+
if (typeof v !== "object") flat[k] = String(v);
|
|
176
|
+
}
|
|
177
|
+
const qs = new URLSearchParams(flat).toString();
|
|
178
|
+
if (qs) url += (url.includes("?") ? "&" : "?") + qs;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { url, method, headers, body };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Internal: audit log (NEVER contains credentials) ─────────
|
|
185
|
+
_logAudit(service, endpoint, status, durationMs) {
|
|
186
|
+
try {
|
|
187
|
+
const entry = {
|
|
188
|
+
ts: new Date().toISOString(),
|
|
189
|
+
service,
|
|
190
|
+
endpoint,
|
|
191
|
+
status,
|
|
192
|
+
durationMs,
|
|
193
|
+
};
|
|
194
|
+
appendFileSync(AUDIT_FILE, JSON.stringify(entry) + "\n");
|
|
195
|
+
} catch {
|
|
196
|
+
// Audit logging should never break execution
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|