@0xmaxma/claude-gateway 1.8.6 → 1.8.8
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 +159 -4
- package/dist/agent/dreaming/config.d.ts +3 -1
- package/dist/agent/dreaming/config.d.ts.map +1 -1
- package/dist/agent/dreaming/config.js +14 -3
- package/dist/agent/dreaming/config.js.map +1 -1
- package/dist/agent/dreaming/index.d.ts +2 -0
- package/dist/agent/dreaming/index.d.ts.map +1 -1
- package/dist/agent/dreaming/index.js +1 -1
- package/dist/agent/dreaming/index.js.map +1 -1
- package/dist/agent/knowledge/config.d.ts +3 -1
- package/dist/agent/knowledge/config.d.ts.map +1 -1
- package/dist/agent/knowledge/config.js +13 -3
- package/dist/agent/knowledge/config.js.map +1 -1
- package/dist/agent/runner.d.ts +66 -1
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +165 -12
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/skill-learning/config.d.ts +3 -1
- package/dist/agent/skill-learning/config.d.ts.map +1 -1
- package/dist/agent/skill-learning/config.js +13 -3
- package/dist/agent/skill-learning/config.js.map +1 -1
- package/dist/agent/skill-learning/index.d.ts +2 -0
- package/dist/agent/skill-learning/index.d.ts.map +1 -1
- package/dist/agent/skill-learning/index.js +1 -1
- package/dist/agent/skill-learning/index.js.map +1 -1
- package/dist/api/connectors-router.d.ts +50 -0
- package/dist/api/connectors-router.d.ts.map +1 -0
- package/dist/api/connectors-router.js +650 -0
- package/dist/api/connectors-router.js.map +1 -0
- package/dist/api/gateway-router.d.ts +4 -0
- package/dist/api/gateway-router.d.ts.map +1 -1
- package/dist/api/gateway-router.js +26 -0
- package/dist/api/gateway-router.js.map +1 -1
- package/dist/api/oauth-connectors-router.d.ts +43 -0
- package/dist/api/oauth-connectors-router.d.ts.map +1 -0
- package/dist/api/oauth-connectors-router.js +384 -0
- package/dist/api/oauth-connectors-router.js.map +1 -0
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +101 -9
- package/dist/api/router.js.map +1 -1
- package/dist/apps/agent-manager.d.ts +6 -2
- package/dist/apps/agent-manager.d.ts.map +1 -1
- package/dist/apps/agent-manager.js +14 -17
- package/dist/apps/agent-manager.js.map +1 -1
- package/dist/apps/installer.d.ts +3 -1
- package/dist/apps/installer.d.ts.map +1 -1
- package/dist/apps/installer.js +8 -2
- package/dist/apps/installer.js.map +1 -1
- package/dist/cli/commands/service.d.ts +34 -4
- package/dist/cli/commands/service.d.ts.map +1 -1
- package/dist/cli/commands/service.js +552 -96
- package/dist/cli/commands/service.js.map +1 -1
- package/dist/config/bootstrap.d.ts.map +1 -1
- package/dist/config/bootstrap.js +4 -1
- package/dist/config/bootstrap.js.map +1 -1
- package/dist/config/config-write-lock.d.ts +45 -0
- package/dist/config/config-write-lock.d.ts.map +1 -0
- package/dist/config/config-write-lock.js +164 -0
- package/dist/config/config-write-lock.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +36 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/migrator.d.ts.map +1 -1
- package/dist/config/migrator.js +12 -1
- package/dist/config/migrator.js.map +1 -1
- package/dist/config/watcher.d.ts.map +1 -1
- package/dist/config/watcher.js +29 -0
- package/dist/config/watcher.js.map +1 -1
- package/dist/connectors/custom-connectors-store.d.ts +54 -0
- package/dist/connectors/custom-connectors-store.d.ts.map +1 -0
- package/dist/connectors/custom-connectors-store.js +204 -0
- package/dist/connectors/custom-connectors-store.js.map +1 -0
- package/dist/connectors/custom.d.ts +69 -0
- package/dist/connectors/custom.d.ts.map +1 -0
- package/dist/connectors/custom.js +158 -0
- package/dist/connectors/custom.js.map +1 -0
- package/dist/connectors/mcp-oauth.d.ts +152 -0
- package/dist/connectors/mcp-oauth.d.ts.map +1 -0
- package/dist/connectors/mcp-oauth.js +522 -0
- package/dist/connectors/mcp-oauth.js.map +1 -0
- package/dist/connectors/oauth-refresh-sweep.d.ts +71 -0
- package/dist/connectors/oauth-refresh-sweep.d.ts.map +1 -0
- package/dist/connectors/oauth-refresh-sweep.js +337 -0
- package/dist/connectors/oauth-refresh-sweep.js.map +1 -0
- package/dist/connectors/pending-oauth-store.d.ts +39 -0
- package/dist/connectors/pending-oauth-store.d.ts.map +1 -0
- package/dist/connectors/pending-oauth-store.js +50 -0
- package/dist/connectors/pending-oauth-store.js.map +1 -0
- package/dist/connectors/resolve.d.ts +52 -0
- package/dist/connectors/resolve.d.ts.map +1 -0
- package/dist/connectors/resolve.js +176 -0
- package/dist/connectors/resolve.js.map +1 -0
- package/dist/connectors/token-env.d.ts +93 -0
- package/dist/connectors/token-env.d.ts.map +1 -0
- package/dist/connectors/token-env.js +323 -0
- package/dist/connectors/token-env.js.map +1 -0
- package/dist/connectors/types.d.ts +126 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +13 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/index.js +37 -4
- package/dist/index.js.map +1 -1
- package/dist/session/process.d.ts +23 -0
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +97 -4
- package/dist/session/process.js.map +1 -1
- package/dist/types.d.ts +46 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/config/watcher.js
CHANGED
|
@@ -14,6 +14,12 @@ const HOT_RELOADABLE_AGENT_FIELDS = [
|
|
|
14
14
|
'session.idleTimeoutMinutes',
|
|
15
15
|
'session.maxConcurrent',
|
|
16
16
|
'heartbeat.rateLimitMinutes',
|
|
17
|
+
// Per-agent connector enablement ({connectorId: {enabled}}) — only affects
|
|
18
|
+
// NEW session spawns (SessionProcess reads it fresh via AgentRunner's own
|
|
19
|
+
// live object reference, see index.ts's 'changes' handler); an
|
|
20
|
+
// already-running session's subprocess still needs an explicit restart
|
|
21
|
+
// (restartSessionsUsingConnector) to pick it up mid-conversation.
|
|
22
|
+
'connectors',
|
|
17
23
|
];
|
|
18
24
|
exports.HOT_RELOADABLE_AGENT_FIELDS = HOT_RELOADABLE_AGENT_FIELDS;
|
|
19
25
|
// Gateway-level (non-agent) fields that can be hot-reloaded; agentId will be '' in ConfigChange
|
|
@@ -23,6 +29,25 @@ const HOT_RELOADABLE_GATEWAY_FIELDS = [
|
|
|
23
29
|
// call — and turning the level up to chase a live problem is precisely when a
|
|
24
30
|
// restart is unaffordable, since it kills the sessions being investigated.
|
|
25
31
|
'gateway.logs',
|
|
32
|
+
// Same "new spawns only" caveat as 'connectors' above — customConnectors is
|
|
33
|
+
// where every connector definition lives regardless of who owns its
|
|
34
|
+
// credential (see connectors/types.ts's CustomConnectorEntry).
|
|
35
|
+
'gateway.customConnectors',
|
|
36
|
+
// The default the per-agent `connectors` map is read against
|
|
37
|
+
// (resolveEnabledConnectors' third argument). Hot-reloadable for the same
|
|
38
|
+
// reason and with the same scope as the two above: both readers —
|
|
39
|
+
// SessionProcess.writeMcpConfig and AgentRunner.restartSessionsUsingConnector
|
|
40
|
+
// — take it off the live config object at call time, so replacing it here is
|
|
41
|
+
// enough for every subsequent spawn. Flipping it to `false` is how an operator
|
|
42
|
+
// shuts every not-explicitly-enabled connector off on a shared box; making
|
|
43
|
+
// that wait for a gateway restart would mean killing the sessions in order to
|
|
44
|
+
// narrow what they can reach.
|
|
45
|
+
//
|
|
46
|
+
// gateway.oauthReturnUrl is deliberately NOT here: createOauthCallbackRouter
|
|
47
|
+
// captures it as a plain argument at mount time (gateway-router.ts), so a live
|
|
48
|
+
// edit cannot reach the mounted router. It is listed in gatewayFieldPairs
|
|
49
|
+
// below so the change is still reported — as restart-required, like publicUrl.
|
|
50
|
+
'gateway.connectorsDefaultEnabled',
|
|
26
51
|
];
|
|
27
52
|
class ConfigWatcher extends events_1.EventEmitter {
|
|
28
53
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -170,6 +195,7 @@ class ConfigWatcher extends events_1.EventEmitter {
|
|
|
170
195
|
{ field: 'telegram.botToken', oldVal: oldAgent.telegram?.botToken, newVal: newAgent.telegram?.botToken },
|
|
171
196
|
{ field: 'discord.botToken', oldVal: oldAgent.discord?.botToken, newVal: newAgent.discord?.botToken },
|
|
172
197
|
{ field: 'description', oldVal: oldAgent.description, newVal: newAgent.description },
|
|
198
|
+
{ field: 'connectors', oldVal: oldAgent.connectors, newVal: newAgent.connectors },
|
|
173
199
|
];
|
|
174
200
|
for (const { field, oldVal, newVal } of fieldPairs) {
|
|
175
201
|
if (!deepEqual(oldVal, newVal)) {
|
|
@@ -188,6 +214,9 @@ class ConfigWatcher extends events_1.EventEmitter {
|
|
|
188
214
|
{ field: 'gateway.headless', oldVal: oldCfg.gateway.headless, newVal: newCfg.gateway.headless },
|
|
189
215
|
{ field: 'gateway.publicUrl', oldVal: oldCfg.gateway.publicUrl, newVal: newCfg.gateway.publicUrl },
|
|
190
216
|
{ field: 'gateway.logs', oldVal: oldCfg.gateway.logs, newVal: newCfg.gateway.logs },
|
|
217
|
+
{ field: 'gateway.customConnectors', oldVal: oldCfg.gateway.customConnectors, newVal: newCfg.gateway.customConnectors },
|
|
218
|
+
{ field: 'gateway.connectorsDefaultEnabled', oldVal: oldCfg.gateway.connectorsDefaultEnabled, newVal: newCfg.gateway.connectorsDefaultEnabled },
|
|
219
|
+
{ field: 'gateway.oauthReturnUrl', oldVal: oldCfg.gateway.oauthReturnUrl, newVal: newCfg.gateway.oauthReturnUrl },
|
|
191
220
|
];
|
|
192
221
|
for (const { field, oldVal, newVal } of gatewayFieldPairs) {
|
|
193
222
|
if (!deepEqual(oldVal, newVal)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/config/watcher.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/config/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { CustomConnectorEntry } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Read-modify-write access to config.json's `gateway.customConnectors` subtree,
|
|
4
|
+
* extracted out of connectors-router.ts so a second router (oauth-connectors-router.ts)
|
|
5
|
+
* can share the exact same write lock instead of racing an independent one —
|
|
6
|
+
* two routers each holding their own `Promise`-chain lock over the same file
|
|
7
|
+
* would defeat the point of serializing writes at all.
|
|
8
|
+
*
|
|
9
|
+
* That lock is `config/config-write-lock.ts`'s, not a private one, because the same
|
|
10
|
+
* argument applies one level up: api/router.ts, agent/runner.ts and
|
|
11
|
+
* apps/agent-manager.ts all rewrite this file too, and a lock this module owned alone
|
|
12
|
+
* would serialise connector writes against each other while still losing them to an
|
|
13
|
+
* agents-API write that happened to interleave.
|
|
14
|
+
*
|
|
15
|
+
* One instance is created per gateway process (see gateway-router.ts's
|
|
16
|
+
* constructor) and threaded into every router that touches customConnectors.
|
|
17
|
+
*/
|
|
18
|
+
export interface CustomConnectorsStore {
|
|
19
|
+
/** Serialised read-modify-write of the whole customConnectors map. */
|
|
20
|
+
mutate(fn: (connectors: Record<string, CustomConnectorEntry>) => void): Promise<void>;
|
|
21
|
+
/** Fresh read — mirrors token-env.ts's "no caching" stance. */
|
|
22
|
+
read(): Promise<Record<string, CustomConnectorEntry>>;
|
|
23
|
+
/**
|
|
24
|
+
* Serialised read-DECIDE-write for one connector: `fn` is handed the entry as it
|
|
25
|
+
* stands INSIDE the write lock, and config.json is rewritten only if `fn` calls
|
|
26
|
+
* `remove()`.
|
|
27
|
+
*
|
|
28
|
+
* The routes that act on an existing connector — DELETE and /connect — branch on
|
|
29
|
+
* what they find there: DELETE reads `credentialOwner` to choose between clearing
|
|
30
|
+
* the secret and removing the entry, and `secretNames` to choose which secrets to
|
|
31
|
+
* clear; /connect reads both to decide whether a pasted token belongs here at all.
|
|
32
|
+
* Taking that read outside the lock makes the decision a snapshot of a state that
|
|
33
|
+
* a concurrent /oauth/receive (the one route that rewrites an existing entry's
|
|
34
|
+
* owner and secret names wholesale) may already have replaced — so DELETE would
|
|
35
|
+
* clear secrets under names the entry no longer declares while keeping an entry it
|
|
36
|
+
* should have removed, and /connect would file a pasted token under a name nothing
|
|
37
|
+
* ever reads again. `read()` remains right for the routes that only report.
|
|
38
|
+
*
|
|
39
|
+
* `fn` runs while the lock is held, so it must not call back into this store or
|
|
40
|
+
* `withConfigWriteLock` — the lock is a promise chain, not a reentrant one, and a
|
|
41
|
+
* nested acquire deadlocks. Slow work that does not need the entry (restarting
|
|
42
|
+
* sessions, in particular) belongs after this resolves.
|
|
43
|
+
*/
|
|
44
|
+
withEntry<T>(connectorId: string, fn: (write: ConnectorWrite) => T | Promise<T>): Promise<T>;
|
|
45
|
+
}
|
|
46
|
+
/** What `withEntry` hands its callback. */
|
|
47
|
+
export interface ConnectorWrite {
|
|
48
|
+
/** The entry as it stands inside the write lock — `undefined` if there is none. */
|
|
49
|
+
readonly entry: CustomConnectorEntry | undefined;
|
|
50
|
+
/** Hard-delete it: the entry AND every agent's enablement flag for it, in this same write. */
|
|
51
|
+
remove(): void;
|
|
52
|
+
}
|
|
53
|
+
export declare function createCustomConnectorsStore(configPath?: string): CustomConnectorsStore;
|
|
54
|
+
//# sourceMappingURL=custom-connectors-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-connectors-store.d.ts","sourceRoot":"","sources":["../../src/connectors/custom-connectors-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGrD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,+DAA+D;IAC/D,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACtD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9F;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACjD,8FAA8F;IAC9F,MAAM,IAAI,IAAI,CAAC;CAChB;AAgCD,wBAAgB,2BAA2B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,qBAAqB,CA4JtF"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createCustomConnectorsStore = createCustomConnectorsStore;
|
|
37
|
+
const fsp = __importStar(require("fs/promises"));
|
|
38
|
+
const config_write_lock_1 = require("../config/config-write-lock");
|
|
39
|
+
/**
|
|
40
|
+
* Re-key a parsed connector map onto a null-prototype object. Every map this module
|
|
41
|
+
* hands out — to a caller of `read()`, to a `mutate` callback — goes through here.
|
|
42
|
+
*
|
|
43
|
+
* Connector ids are slugs derived from a user-supplied label, and `isValidConnectorId`
|
|
44
|
+
* accepts `constructor`, `toString`, `valueOf` — all inherited properties of a plain
|
|
45
|
+
* `JSON.parse` result. Every lookup in the routers is a bare `map[id]`, so an id like
|
|
46
|
+
* that resolved to a Function rather than to nothing: `GET /:id/status` answered
|
|
47
|
+
* `500 Connector has an unreadable configuration`, DELETE answered
|
|
48
|
+
* `500 Cannot read properties of undefined (reading 'map')` and `/connect`
|
|
49
|
+
* `... (reading 'length')` — both leaking an internal error message — where the honest
|
|
50
|
+
* answer to all three is 404. A null-prototype map has nothing to inherit, so `map[id]`
|
|
51
|
+
* is undefined exactly when the connector does not exist.
|
|
52
|
+
*
|
|
53
|
+
* `Object.assign` copies own enumerable keys only, which is also what `JSON.stringify`
|
|
54
|
+
* writes back out, so a map that has been through here round-trips unchanged.
|
|
55
|
+
*/
|
|
56
|
+
function ownKeysOnly(map) {
|
|
57
|
+
return Object.assign(Object.create(null), map);
|
|
58
|
+
}
|
|
59
|
+
function createCustomConnectorsStore(configPath) {
|
|
60
|
+
// Per store instance, not per module: one store exists per gateway process, so this
|
|
61
|
+
// is process-wide in production, while tests that build their own store each start
|
|
62
|
+
// from a clean throttle instead of inheriting a previous test's timestamp.
|
|
63
|
+
const READ_FAILURE_LOG_INTERVAL_MS = 60 * 1000;
|
|
64
|
+
let lastReadFailureLog = 0;
|
|
65
|
+
async function mutate(fn) {
|
|
66
|
+
if (!configPath)
|
|
67
|
+
return; // no persistence target (e.g. tests) — secret store is authoritative
|
|
68
|
+
return (0, config_write_lock_1.withConfigWriteLock)(configPath, async () => {
|
|
69
|
+
const raw = await fsp.readFile(configPath, 'utf-8');
|
|
70
|
+
const config = JSON.parse(raw);
|
|
71
|
+
config.gateway = config.gateway ?? {};
|
|
72
|
+
// The callback both reads (`slugify(label, Object.keys(c))`, /oauth/receive's
|
|
73
|
+
// `c[id]` capture of the entry it replaces) and writes, so it gets the same
|
|
74
|
+
// null-prototype treatment `read()` gives — see ownKeysOnly.
|
|
75
|
+
const connectors = ownKeysOnly(config.gateway.customConnectors);
|
|
76
|
+
config.gateway.customConnectors = connectors;
|
|
77
|
+
fn(connectors);
|
|
78
|
+
await (0, config_write_lock_1.writeConfigAtomic)(configPath, config);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function read() {
|
|
82
|
+
if (!configPath)
|
|
83
|
+
return ownKeysOnly(undefined);
|
|
84
|
+
try {
|
|
85
|
+
const raw = await fsp.readFile(configPath, 'utf-8');
|
|
86
|
+
const config = JSON.parse(raw);
|
|
87
|
+
return ownKeysOnly(config.gateway?.customConnectors);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
noteReadFailure(err);
|
|
91
|
+
return ownKeysOnly(undefined);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Degrading to {} is right — see readTokenEnv()'s doc for the same argument: this
|
|
96
|
+
* feeds `GET /v1/connectors`, and a throw from an async Express 4 handler reaches
|
|
97
|
+
* index.ts's `unhandledRejection` hook and shuts the gateway down.
|
|
98
|
+
*
|
|
99
|
+
* But degrading SILENTLY is not. An EACCES config.json (one `sudo`, a restored
|
|
100
|
+
* volume) or a hand-edit that left invalid JSON both land here, and the caller
|
|
101
|
+
* cannot tell "no connectors are configured" from "the file that lists them is
|
|
102
|
+
* unreadable": the caller gets an empty list and the refresh sweep concludes
|
|
103
|
+
* there is nothing to refresh, with nothing written anywhere to say why. It
|
|
104
|
+
* also means connectors-router.ts's `500 Connector configuration could not be
|
|
105
|
+
* read` — the documented answer for exactly this — is unreachable, because
|
|
106
|
+
* this function never throws.
|
|
107
|
+
*
|
|
108
|
+
* A missing file is not that: it is the ordinary pre-first-write state, and
|
|
109
|
+
* says the same thing as an empty map.
|
|
110
|
+
*/
|
|
111
|
+
function noteReadFailure(err) {
|
|
112
|
+
if (err.code === 'ENOENT')
|
|
113
|
+
return;
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
// Throttled for readTokenEnv's reason — status pollers hit this every couple
|
|
116
|
+
// of seconds, so one line per failure buries the log it belongs in.
|
|
117
|
+
if (now - lastReadFailureLog < READ_FAILURE_LOG_INTERVAL_MS)
|
|
118
|
+
return;
|
|
119
|
+
lastReadFailureLog = now;
|
|
120
|
+
console.error(`custom-connectors-store: cannot read connectors from ${configPath}` +
|
|
121
|
+
` (${err.code ?? 'invalid JSON'}) — reporting no` +
|
|
122
|
+
` connectors until it is readable: ${err.message}`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The entry AND every agent's enablement flag for it. Returns whether anything went.
|
|
126
|
+
*
|
|
127
|
+
* Both halves, because per-agent enablement is stored separately from the
|
|
128
|
+
* connector entry itself (AgentConfig.connectors vs gateway.customConnectors),
|
|
129
|
+
* so removing only the entry leaves its enablement flags behind as orphans.
|
|
130
|
+
* They are inert while the id is unused — but ids are slugs derived from
|
|
131
|
+
* labels, so re-adding a connector with the same label revives whatever the
|
|
132
|
+
* old one's flags said. An agent that was explicitly disabled for the deleted
|
|
133
|
+
* connector would then start out disabled for the brand-new one, for no reason
|
|
134
|
+
* its owner can see.
|
|
135
|
+
*
|
|
136
|
+
* One write rather than two, because two would reintroduce the very orphan
|
|
137
|
+
* this exists to prevent: a crash — or the emergency shutdown index.ts runs on
|
|
138
|
+
* an unhandled rejection — landing between them leaves the entry gone and the
|
|
139
|
+
* flags behind, and with the entry gone nothing will ever come back for them.
|
|
140
|
+
*
|
|
141
|
+
* Only for a real delete of the entry. A soft disconnect keeps the entry and so
|
|
142
|
+
* must keep its enablement.
|
|
143
|
+
*/
|
|
144
|
+
function dropConnector(config, connectorId) {
|
|
145
|
+
let changed = false;
|
|
146
|
+
// `hasOwnProperty`, not `in`: `'constructor' in {}` is true, so the `in` form
|
|
147
|
+
// deleted nothing (delete on an inherited property is a no-op), reported
|
|
148
|
+
// `changed`, and rewrote config.json for it — see ownKeysOnly for the same
|
|
149
|
+
// hazard on the read side.
|
|
150
|
+
if (config.gateway?.customConnectors &&
|
|
151
|
+
Object.prototype.hasOwnProperty.call(config.gateway.customConnectors, connectorId)) {
|
|
152
|
+
delete config.gateway.customConnectors[connectorId];
|
|
153
|
+
changed = true;
|
|
154
|
+
}
|
|
155
|
+
for (const agent of config.agents ?? []) {
|
|
156
|
+
if (agent.connectors && Object.prototype.hasOwnProperty.call(agent.connectors, connectorId)) {
|
|
157
|
+
delete agent.connectors[connectorId];
|
|
158
|
+
changed = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return changed;
|
|
162
|
+
}
|
|
163
|
+
async function withEntry(connectorId, fn) {
|
|
164
|
+
if (!configPath) {
|
|
165
|
+
// No persistence target (tests): the same "there is nothing there" `read()`
|
|
166
|
+
// reports, and a `remove()` that has nothing to remove.
|
|
167
|
+
return fn({ entry: undefined, remove: () => { } });
|
|
168
|
+
}
|
|
169
|
+
return (0, config_write_lock_1.withConfigWriteLock)(configPath, async () => {
|
|
170
|
+
let config = null;
|
|
171
|
+
try {
|
|
172
|
+
config = JSON.parse(await fsp.readFile(configPath, 'utf-8'));
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
// Same degrade-and-log as read(): the callers reach this through a route
|
|
176
|
+
// that would already have answered 404 off `read()`'s empty map, so an
|
|
177
|
+
// unreadable config keeps meaning "no such connector" rather than
|
|
178
|
+
// rejecting into index.ts's unhandledRejection hook.
|
|
179
|
+
noteReadFailure(err);
|
|
180
|
+
}
|
|
181
|
+
const connectors = ownKeysOnly(config?.gateway?.customConnectors);
|
|
182
|
+
let removeRequested = false;
|
|
183
|
+
const result = await fn({
|
|
184
|
+
entry: connectors[connectorId],
|
|
185
|
+
remove: () => {
|
|
186
|
+
removeRequested = true;
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
if (!removeRequested)
|
|
190
|
+
return result; // decided to keep it — nothing to write
|
|
191
|
+
if (!config) {
|
|
192
|
+
// Unreachable through the routes (no entry to act on means they 404 first),
|
|
193
|
+
// and a lie if it ever were: answering "removed" for a file we could not
|
|
194
|
+
// read. Surfaces as a 500 instead.
|
|
195
|
+
throw new Error(`Cannot remove connector '${connectorId}': ${configPath} is unreadable`);
|
|
196
|
+
}
|
|
197
|
+
if (dropConnector(config, connectorId))
|
|
198
|
+
await (0, config_write_lock_1.writeConfigAtomic)(configPath, config);
|
|
199
|
+
return result;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return { mutate, read, withEntry };
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=custom-connectors-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-connectors-store.js","sourceRoot":"","sources":["../../src/connectors/custom-connectors-store.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuFA,kEA4JC;AAnPD,iDAAmC;AAEnC,mEAAqF;AAuDrF;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,WAAW,CAClB,GAAqD;IAErD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAyC,EAAE,GAAG,CAAC,CAAC;AACzF,CAAC;AASD,SAAgB,2BAA2B,CAAC,UAAmB;IAC7D,oFAAoF;IACpF,mFAAmF;IACnF,2EAA2E;IAC3E,MAAM,4BAA4B,GAAG,EAAE,GAAG,IAAI,CAAC;IAC/C,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAE3B,KAAK,UAAU,MAAM,CACnB,EAA8D;QAE9D,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,qEAAqE;QAC9F,OAAO,IAAA,uCAAmB,EAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAG5B,CAAC;YACF,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;YACtC,8EAA8E;YAC9E,4EAA4E;YAC5E,6DAA6D;YAC7D,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAChE,MAAM,CAAC,OAAO,CAAC,gBAAgB,GAAG,UAAU,CAAC;YAC7C,EAAE,CAAC,UAAU,CAAC,CAAC;YACf,MAAM,IAAA,qCAAiB,EAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,UAAU,IAAI;QACjB,IAAI,CAAC,UAAU;YAAE,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAE5B,CAAC;YACF,OAAO,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,eAAe,CAAC,GAAG,CAAC,CAAC;YACrB,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,eAAe,CAAC,GAAY;QACnC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,6EAA6E;QAC7E,oEAAoE;QACpE,IAAI,GAAG,GAAG,kBAAkB,GAAG,4BAA4B;YAAE,OAAO;QACpE,kBAAkB,GAAG,GAAG,CAAC;QACzB,OAAO,CAAC,KAAK,CACX,wDAAwD,UAAU,EAAE;YAClE,KAAM,GAA6B,CAAC,IAAI,IAAI,cAAc,kBAAkB;YAC5E,qCAAsC,GAAa,CAAC,OAAO,EAAE,CAChE,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,aAAa,CAAC,MAAoB,EAAE,WAAmB;QAC9D,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,8EAA8E;QAC9E,yEAAyE;QACzE,2EAA2E;QAC3E,2BAA2B;QAC3B,IACE,MAAM,CAAC,OAAO,EAAE,gBAAgB;YAChC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,EAClF,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;YACpD,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;gBAC5F,OAAO,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;gBACrC,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,UAAU,SAAS,CACtB,WAAmB,EACnB,EAA6C;QAE7C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,4EAA4E;YAC5E,wDAAwD;YACxD,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAA,uCAAmB,EAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YAChD,IAAI,MAAM,GAAwB,IAAI,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAiB,CAAC;YAC/E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,yEAAyE;gBACzE,uEAAuE;gBACvE,kEAAkE;gBAClE,qDAAqD;gBACrD,eAAe,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YACD,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;YAClE,IAAI,eAAe,GAAG,KAAK,CAAC;YAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;gBACtB,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC;gBAC9B,MAAM,EAAE,GAAG,EAAE;oBACX,eAAe,GAAG,IAAI,CAAC;gBACzB,CAAC;aACF,CAAC,CAAC;YACH,IAAI,CAAC,eAAe;gBAAE,OAAO,MAAM,CAAC,CAAC,wCAAwC;YAC7E,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,4EAA4E;gBAC5E,yEAAyE;gBACzE,mCAAmC;gBACnC,MAAM,IAAI,KAAK,CAAC,4BAA4B,WAAW,MAAM,UAAU,gBAAgB,CAAC,CAAC;YAC3F,CAAC;YACD,IAAI,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC;gBAAE,MAAM,IAAA,qCAAiB,EAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpF,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom (user-pasted) connector helpers — id generation, placeholder
|
|
3
|
+
* extraction, and secret substitution. These entries are admin-trusted data,
|
|
4
|
+
* not code — see CustomConnectorEntry's doc comment in connectors/types.ts.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* MCP server names the gateway writes into every session's mcp-config.json itself
|
|
8
|
+
* (see session/process.ts's writeMcpConfig). That writer drops any injected entry whose
|
|
9
|
+
* key collides — a correct guard, but a silent one: a connector landing on one of these
|
|
10
|
+
* ids is created, stores its secret, and reports "Connected ✓" while never reaching a
|
|
11
|
+
* session, unfixable except by deleting it. Reserving the names where ids are minted
|
|
12
|
+
* turns that into a different, working id.
|
|
13
|
+
*
|
|
14
|
+
* Exported so process.ts's skip list is this same set, not a second copy of the
|
|
15
|
+
* literals — a name added there but not here silently reopens the hole.
|
|
16
|
+
*/
|
|
17
|
+
export declare const RESERVED_CONNECTOR_IDS: ReadonlySet<string>;
|
|
18
|
+
export declare function isReservedConnectorId(id: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* A slug not already used by a reserved id or an existing custom id. Appends
|
|
21
|
+
* -2, -3, ... on collision (custom ids are user-facing, not secret, so a readable
|
|
22
|
+
* suffix beats a random one).
|
|
23
|
+
*
|
|
24
|
+
* The result is always a valid connector id, length included. Every management route
|
|
25
|
+
* takes the id back from the URL and runs it through `isValidConnectorId`, so an id
|
|
26
|
+
* this function emits but that one rejects produces a connector that resolves into
|
|
27
|
+
* sessions and works — yet whose status, connect, delete and oauth/start routes all
|
|
28
|
+
* answer 400, fixable only by hand-editing config.json. The base is therefore truncated
|
|
29
|
+
* with the collision suffix's width already reserved.
|
|
30
|
+
*
|
|
31
|
+
* Managed ids like 'github'/'gmail' are NOT reserved here — an external control plane
|
|
32
|
+
* pushes those in, so a user-added connector could slug-collide with one. Accepted as a
|
|
33
|
+
* self-recoverable edge case (both paths are admin-trusted) rather than hardcoding
|
|
34
|
+
* another system's ids. RESERVED_CONNECTOR_IDS is reserved because that collision is
|
|
35
|
+
* NOT self-recoverable.
|
|
36
|
+
*/
|
|
37
|
+
export declare function slugify(label: string, existingCustomIds: Iterable<string>): string;
|
|
38
|
+
/** Recursively collect every unique {name} placeholder found in string values. */
|
|
39
|
+
export declare function extractPlaceholders(config: unknown): string[];
|
|
40
|
+
/** Recursively replace every {name} in string values with secrets[name] (or '' if absent). */
|
|
41
|
+
export declare function substitutePlaceholders(config: Record<string, unknown>, secrets: Record<string, string>): Record<string, unknown>;
|
|
42
|
+
/** Namespaced mcp-token.env key so two custom connectors reusing {api_key} don't collide. */
|
|
43
|
+
export declare function customSecretKey(id: string, name: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Namespaced key for gateway-internal bookkeeping about a connector (the refresh
|
|
46
|
+
* token, its expiry, failure counters — see oauth-refresh-sweep.ts), stored in the
|
|
47
|
+
* same file but under a prefix `customSecretKey()` can never produce.
|
|
48
|
+
*
|
|
49
|
+
* The separate prefix is the whole point: `PLACEHOLDER_RE` accepts a leading
|
|
50
|
+
* underscore, so a pasted `{__refresh_token}` DOES yield `__refresh_token` as a
|
|
51
|
+
* secretName. Sharing one prefix would make `customSecretKey(id, '__refresh_token')`
|
|
52
|
+
* and the sweep's own slot the same string — a pasted config that resolves the
|
|
53
|
+
* gateway's refresh token into an outbound header. `isReservedPlaceholder` reports that
|
|
54
|
+
* at add-time; this split makes it unreachable even if a caller forgets to validate.
|
|
55
|
+
*/
|
|
56
|
+
export declare function internalSecretKey(id: string, name: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Placeholder names the gateway reserves for itself. Rejected when adding or pushing
|
|
59
|
+
* a connector so the admin gets a clear 400 instead of a placeholder that silently
|
|
60
|
+
* resolves to the empty string.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isReservedPlaceholder(name: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Upper bound shared by the generator (`slugify`) and the validator below so the two
|
|
65
|
+
* can't drift — see slugify's doc comment for what happens when they do.
|
|
66
|
+
*/
|
|
67
|
+
export declare const MAX_CONNECTOR_ID_LENGTH = 64;
|
|
68
|
+
export declare function isValidConnectorId(id: string): boolean;
|
|
69
|
+
//# sourceMappingURL=custom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/connectors/custom.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqBH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAoC,CAAC;AAE5F,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAalF;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAa7D;AAED,8FAA8F;AAC9F,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAUD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Custom (user-pasted) connector helpers — id generation, placeholder
|
|
4
|
+
* extraction, and secret substitution. These entries are admin-trusted data,
|
|
5
|
+
* not code — see CustomConnectorEntry's doc comment in connectors/types.ts.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.MAX_CONNECTOR_ID_LENGTH = exports.RESERVED_CONNECTOR_IDS = void 0;
|
|
9
|
+
exports.isReservedConnectorId = isReservedConnectorId;
|
|
10
|
+
exports.slugify = slugify;
|
|
11
|
+
exports.extractPlaceholders = extractPlaceholders;
|
|
12
|
+
exports.substitutePlaceholders = substitutePlaceholders;
|
|
13
|
+
exports.customSecretKey = customSecretKey;
|
|
14
|
+
exports.internalSecretKey = internalSecretKey;
|
|
15
|
+
exports.isReservedPlaceholder = isReservedPlaceholder;
|
|
16
|
+
exports.isValidConnectorId = isValidConnectorId;
|
|
17
|
+
const PLACEHOLDER_RE = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
18
|
+
/**
|
|
19
|
+
* Lowercase, dash-separated id from a human label, e.g. "Google Calendar!" → "google-calendar".
|
|
20
|
+
*
|
|
21
|
+
* Truncated to `budget` characters (and re-trimmed, so a cut that lands on a dash
|
|
22
|
+
* doesn't leave a trailing one). See `slugify` for why the cap is not optional.
|
|
23
|
+
*/
|
|
24
|
+
function slugBase(label, budget) {
|
|
25
|
+
const slug = label
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.trim()
|
|
28
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
29
|
+
.replace(/^-+|-+$/g, '')
|
|
30
|
+
.slice(0, budget)
|
|
31
|
+
.replace(/-+$/g, '');
|
|
32
|
+
return slug || 'connector';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* MCP server names the gateway writes into every session's mcp-config.json itself
|
|
36
|
+
* (see session/process.ts's writeMcpConfig). That writer drops any injected entry whose
|
|
37
|
+
* key collides — a correct guard, but a silent one: a connector landing on one of these
|
|
38
|
+
* ids is created, stores its secret, and reports "Connected ✓" while never reaching a
|
|
39
|
+
* session, unfixable except by deleting it. Reserving the names where ids are minted
|
|
40
|
+
* turns that into a different, working id.
|
|
41
|
+
*
|
|
42
|
+
* Exported so process.ts's skip list is this same set, not a second copy of the
|
|
43
|
+
* literals — a name added there but not here silently reopens the hole.
|
|
44
|
+
*/
|
|
45
|
+
exports.RESERVED_CONNECTOR_IDS = new Set(['gateway', 'telegram']);
|
|
46
|
+
function isReservedConnectorId(id) {
|
|
47
|
+
return exports.RESERVED_CONNECTOR_IDS.has(id);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A slug not already used by a reserved id or an existing custom id. Appends
|
|
51
|
+
* -2, -3, ... on collision (custom ids are user-facing, not secret, so a readable
|
|
52
|
+
* suffix beats a random one).
|
|
53
|
+
*
|
|
54
|
+
* The result is always a valid connector id, length included. Every management route
|
|
55
|
+
* takes the id back from the URL and runs it through `isValidConnectorId`, so an id
|
|
56
|
+
* this function emits but that one rejects produces a connector that resolves into
|
|
57
|
+
* sessions and works — yet whose status, connect, delete and oauth/start routes all
|
|
58
|
+
* answer 400, fixable only by hand-editing config.json. The base is therefore truncated
|
|
59
|
+
* with the collision suffix's width already reserved.
|
|
60
|
+
*
|
|
61
|
+
* Managed ids like 'github'/'gmail' are NOT reserved here — an external control plane
|
|
62
|
+
* pushes those in, so a user-added connector could slug-collide with one. Accepted as a
|
|
63
|
+
* self-recoverable edge case (both paths are admin-trusted) rather than hardcoding
|
|
64
|
+
* another system's ids. RESERVED_CONNECTOR_IDS is reserved because that collision is
|
|
65
|
+
* NOT self-recoverable.
|
|
66
|
+
*/
|
|
67
|
+
function slugify(label, existingCustomIds) {
|
|
68
|
+
const taken = new Set(exports.RESERVED_CONNECTOR_IDS);
|
|
69
|
+
for (const id of existingCustomIds)
|
|
70
|
+
taken.add(id);
|
|
71
|
+
const base = slugBase(label, exports.MAX_CONNECTOR_ID_LENGTH);
|
|
72
|
+
if (!taken.has(base))
|
|
73
|
+
return base;
|
|
74
|
+
for (let n = 2;; n++) {
|
|
75
|
+
const suffix = `-${n}`;
|
|
76
|
+
// Re-slug against a smaller budget rather than trimming `base`, so the cut
|
|
77
|
+
// still lands on a clean boundary as the suffix grows past one digit.
|
|
78
|
+
const candidate = `${slugBase(label, exports.MAX_CONNECTOR_ID_LENGTH - suffix.length)}${suffix}`;
|
|
79
|
+
if (!taken.has(candidate))
|
|
80
|
+
return candidate;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Recursively collect every unique {name} placeholder found in string values. */
|
|
84
|
+
function extractPlaceholders(config) {
|
|
85
|
+
const found = new Set();
|
|
86
|
+
const walk = (value) => {
|
|
87
|
+
if (typeof value === 'string') {
|
|
88
|
+
for (const m of value.matchAll(PLACEHOLDER_RE))
|
|
89
|
+
found.add(m[1]);
|
|
90
|
+
}
|
|
91
|
+
else if (Array.isArray(value)) {
|
|
92
|
+
value.forEach(walk);
|
|
93
|
+
}
|
|
94
|
+
else if (value && typeof value === 'object') {
|
|
95
|
+
Object.values(value).forEach(walk);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
walk(config);
|
|
99
|
+
return [...found];
|
|
100
|
+
}
|
|
101
|
+
/** Recursively replace every {name} in string values with secrets[name] (or '' if absent). */
|
|
102
|
+
function substitutePlaceholders(config, secrets) {
|
|
103
|
+
const walk = (value) => {
|
|
104
|
+
if (typeof value === 'string') {
|
|
105
|
+
return value.replace(PLACEHOLDER_RE, (_match, name) => secrets[name] ?? '');
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(value))
|
|
108
|
+
return value.map(walk);
|
|
109
|
+
if (value && typeof value === 'object') {
|
|
110
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, walk(v)]));
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
};
|
|
114
|
+
return walk(config);
|
|
115
|
+
}
|
|
116
|
+
/** Namespaced mcp-token.env key so two custom connectors reusing {api_key} don't collide. */
|
|
117
|
+
function customSecretKey(id, name) {
|
|
118
|
+
return `CUSTOM__${id}__${name}`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Namespaced key for gateway-internal bookkeeping about a connector (the refresh
|
|
122
|
+
* token, its expiry, failure counters — see oauth-refresh-sweep.ts), stored in the
|
|
123
|
+
* same file but under a prefix `customSecretKey()` can never produce.
|
|
124
|
+
*
|
|
125
|
+
* The separate prefix is the whole point: `PLACEHOLDER_RE` accepts a leading
|
|
126
|
+
* underscore, so a pasted `{__refresh_token}` DOES yield `__refresh_token` as a
|
|
127
|
+
* secretName. Sharing one prefix would make `customSecretKey(id, '__refresh_token')`
|
|
128
|
+
* and the sweep's own slot the same string — a pasted config that resolves the
|
|
129
|
+
* gateway's refresh token into an outbound header. `isReservedPlaceholder` reports that
|
|
130
|
+
* at add-time; this split makes it unreachable even if a caller forgets to validate.
|
|
131
|
+
*/
|
|
132
|
+
function internalSecretKey(id, name) {
|
|
133
|
+
return `CUSTOMINT__${id}__${name}`;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Placeholder names the gateway reserves for itself. Rejected when adding or pushing
|
|
137
|
+
* a connector so the admin gets a clear 400 instead of a placeholder that silently
|
|
138
|
+
* resolves to the empty string.
|
|
139
|
+
*/
|
|
140
|
+
function isReservedPlaceholder(name) {
|
|
141
|
+
return name.startsWith('__');
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Connector ids are config.json object keys and are interpolated into mcp-token.env
|
|
145
|
+
* key names, so they are constrained to the shape `slugify()` produces. Routes taking
|
|
146
|
+
* an `:id` from the URL must check this first — `/oauth/receive` in particular names a
|
|
147
|
+
* connector it is creating, so there is no existing entry to validate against.
|
|
148
|
+
*/
|
|
149
|
+
const CONNECTOR_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
150
|
+
/**
|
|
151
|
+
* Upper bound shared by the generator (`slugify`) and the validator below so the two
|
|
152
|
+
* can't drift — see slugify's doc comment for what happens when they do.
|
|
153
|
+
*/
|
|
154
|
+
exports.MAX_CONNECTOR_ID_LENGTH = 64;
|
|
155
|
+
function isValidConnectorId(id) {
|
|
156
|
+
return id.length <= exports.MAX_CONNECTOR_ID_LENGTH && CONNECTOR_ID_RE.test(id);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/connectors/custom.ts"],"names":[],"mappings":"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"}
|