@zackbart/connecta 0.24.3 → 0.25.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/AGENTS.md +58 -30
- package/CHANGELOG.md +317 -1
- package/README.md +5 -6
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +141 -105
- package/dist/catalog.js +258 -33
- package/dist/concurrency.js +24 -23
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +3 -3
- package/dist/connectors/api.js +10 -2
- package/dist/connectors/guarded-fetch.js +34 -4
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +325 -245
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +11 -3
- package/dist/errors.js +2 -1
- package/dist/execute.js +363 -257
- package/dist/executor-admission.js +67 -50
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +251 -210
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +63 -21
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +304 -114
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +46 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +109 -2
- package/dist/program-source.js +205 -0
- package/dist/providers/cloudflare.d.ts +14 -2
- package/dist/providers/cloudflare.js +107 -16
- package/dist/providers/linear.d.ts +26 -4
- package/dist/providers/linear.js +19 -4
- package/dist/providers/mixpanel.d.ts +16 -3
- package/dist/providers/mixpanel.js +13 -2
- package/dist/providers/notion.d.ts +8 -1
- package/dist/providers/notion.js +85 -12
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +70 -5
- package/dist/providers/stripe.d.ts +10 -3
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +12 -2
- package/dist/registry.d.ts +13 -5
- package/dist/registry.js +317 -258
- package/dist/routes/activity.js +120 -137
- package/dist/routes/credentials.js +174 -163
- package/dist/routes/mcp.js +129 -70
- package/dist/routes/oauth-management.js +60 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +82 -0
- package/dist/routes/shared.d.ts +2 -0
- package/dist/routes/ui-data.js +210 -0
- package/dist/routes/ui.js +64 -32
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +111 -89
- package/dist/skills.js +1 -1
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +17 -47
- package/dist/types.d.ts +64 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +68 -176
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +368 -182
- package/documentation/auth.md +277 -176
- package/documentation/code-mode.md +479 -329
- package/documentation/meta-tools.md +494 -417
- package/ethos.md +44 -47
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +62 -14
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +18 -21
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -4
- package/templates/node/.env.example +3 -3
- package/templates/node/AGENTS.md +5 -4
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +23 -22
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -92
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -151
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
- package/dist/tool-safety.d.ts +0 -9
- package/documentation/call-admission.md +0 -158
- package/documentation/cloudflare.md +0 -471
- package/documentation/connector-guides.md +0 -176
- package/documentation/connectors.md +0 -431
- package/documentation/linear.md +0 -193
- package/documentation/mixpanel.md +0 -160
- package/documentation/notion.md +0 -308
- package/documentation/operations.md +0 -359
- package/documentation/operator-ui.md +0 -135
- package/documentation/optional-modules-upgrade.md +0 -243
- package/documentation/provider-conventions.md +0 -729
- package/documentation/request-admission.md +0 -204
- package/documentation/revenuecat.md +0 -305
- package/documentation/storage-and-credentials.md +0 -254
- package/documentation/stripe.md +0 -262
- package/documentation/upgrading.md +0 -768
- package/documentation/vercel.md +0 -241
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntactic destination checks for URLs a downstream hands connecta to fetch.
|
|
3
|
+
*
|
|
4
|
+
* A remote MCP server's OAuth metadata names further URLs — authorization
|
|
5
|
+
* servers, token and registration endpoints — that connecta then requests
|
|
6
|
+
* server-side. Config is the security model: the connector's configured
|
|
7
|
+
* origin is trusted because an operator wrote it down. Everything else was
|
|
8
|
+
* learned from the downstream, and a compromised downstream must not be able
|
|
9
|
+
* to steer connecta at the host's own network (cloud metadata, a LAN admin
|
|
10
|
+
* panel, a sidecar on loopback).
|
|
11
|
+
*
|
|
12
|
+
* The check is by syntax only. The WHATWG URL parser has already folded every
|
|
13
|
+
* legacy IPv4 spelling (`2130706433`, `0x7f.1`, `0177.0.0.1`) into dotted
|
|
14
|
+
* decimal and every IPv6 literal into compressed hex, so one canonical form is
|
|
15
|
+
* all this module reads. It never resolves a name: that needs DNS, which the
|
|
16
|
+
* Workers-safe core cannot do, and a name that resolves somewhere private — or
|
|
17
|
+
* rebinds after the check — is out of scope. Pure Web API, no `node:` import.
|
|
18
|
+
*/
|
|
19
|
+
function classifyIpv4(octets) {
|
|
20
|
+
const [a = 0, b = 0] = octets;
|
|
21
|
+
if (a === 127)
|
|
22
|
+
return "loopback";
|
|
23
|
+
if (a === 0 ||
|
|
24
|
+
a === 10 ||
|
|
25
|
+
(a === 172 && b >= 16 && b <= 31) ||
|
|
26
|
+
(a === 192 && b === 168) ||
|
|
27
|
+
(a === 169 && b === 254) ||
|
|
28
|
+
(a === 100 && b >= 64 && b <= 127)) {
|
|
29
|
+
return "private";
|
|
30
|
+
}
|
|
31
|
+
return "public";
|
|
32
|
+
}
|
|
33
|
+
const IPV4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
34
|
+
const HEXTET = /^[0-9a-f]{1,4}$/;
|
|
35
|
+
function ipv4Octets(host) {
|
|
36
|
+
const match = IPV4.exec(host);
|
|
37
|
+
if (!match)
|
|
38
|
+
return undefined;
|
|
39
|
+
const octets = match.slice(1).map(Number);
|
|
40
|
+
return octets.every((octet) => octet <= 255) ? octets : undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Eight 16-bit pieces of a bracketed IPv6 literal, or undefined. */
|
|
43
|
+
function ipv6Pieces(host) {
|
|
44
|
+
const body = host.slice(1, -1);
|
|
45
|
+
const halves = body.split("::");
|
|
46
|
+
if (halves.length > 2)
|
|
47
|
+
return undefined;
|
|
48
|
+
const pieces = (part) => {
|
|
49
|
+
if (!part)
|
|
50
|
+
return [];
|
|
51
|
+
const parsed = part.split(":").map((piece) => HEXTET.test(piece) ? Number.parseInt(piece, 16) : Number.NaN);
|
|
52
|
+
return parsed.some(Number.isNaN) ? undefined : parsed;
|
|
53
|
+
};
|
|
54
|
+
const head = pieces(halves[0]);
|
|
55
|
+
const tail = pieces(halves[1]);
|
|
56
|
+
if (!head || !tail)
|
|
57
|
+
return undefined;
|
|
58
|
+
const missing = 8 - head.length - tail.length;
|
|
59
|
+
if (halves.length === 1 ? missing !== 0 : missing < 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
return [...head, ...Array.from({ length: missing }, () => 0), ...tail];
|
|
62
|
+
}
|
|
63
|
+
function classifyIpv6(pieces) {
|
|
64
|
+
const [first = 0] = pieces;
|
|
65
|
+
const leadingZeros = pieces.slice(0, 5).every((piece) => piece === 0);
|
|
66
|
+
if (leadingZeros && pieces[5] === 0 && pieces[6] === 0) {
|
|
67
|
+
if (pieces[7] === 0)
|
|
68
|
+
return "private"; // ::
|
|
69
|
+
if (pieces[7] === 1)
|
|
70
|
+
return "loopback"; // ::1
|
|
71
|
+
}
|
|
72
|
+
// ::ffff:a.b.c.d reaches the mapped IPv4 destination; classify that.
|
|
73
|
+
if (leadingZeros && pieces[5] === 0xffff) {
|
|
74
|
+
const high = pieces[6] ?? 0;
|
|
75
|
+
const low = pieces[7] ?? 0;
|
|
76
|
+
return classifyIpv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);
|
|
77
|
+
}
|
|
78
|
+
if ((first & 0xfe00) === 0xfc00)
|
|
79
|
+
return "private"; // fc00::/7
|
|
80
|
+
if ((first & 0xffc0) === 0xfe80)
|
|
81
|
+
return "private"; // fe80::/10
|
|
82
|
+
return "public";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The hostname the WHATWG parser settles on. Idempotent for a `URL.hostname`;
|
|
86
|
+
* anything else — a bare IPv6 literal, a legacy IPv4 spelling — is folded the
|
|
87
|
+
* same way the parser would fold it on the way to a fetch.
|
|
88
|
+
*/
|
|
89
|
+
function canonicalHost(hostname) {
|
|
90
|
+
if (/[/\\?#@]/.test(hostname))
|
|
91
|
+
return undefined;
|
|
92
|
+
const bracketed = hostname.includes(":") && !hostname.startsWith("[")
|
|
93
|
+
? `[${hostname}]`
|
|
94
|
+
: hostname;
|
|
95
|
+
try {
|
|
96
|
+
return new URL(`http://${bracketed}/`).hostname;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Classify a URL host by syntax. A host that does not parse is `private`:
|
|
104
|
+
* the check fails closed rather than guessing.
|
|
105
|
+
*/
|
|
106
|
+
export function classifyHost(hostname) {
|
|
107
|
+
const host = canonicalHost(hostname);
|
|
108
|
+
if (!host)
|
|
109
|
+
return "private";
|
|
110
|
+
if (host.startsWith("[")) {
|
|
111
|
+
const pieces = ipv6Pieces(host);
|
|
112
|
+
return pieces ? classifyIpv6(pieces) : "private";
|
|
113
|
+
}
|
|
114
|
+
const octets = ipv4Octets(host);
|
|
115
|
+
if (octets)
|
|
116
|
+
return classifyIpv4(octets);
|
|
117
|
+
const name = host.endsWith(".") ? host.slice(0, -1) : host;
|
|
118
|
+
if (name === "localhost" || name.endsWith(".localhost"))
|
|
119
|
+
return "loopback";
|
|
120
|
+
return "public";
|
|
121
|
+
}
|
|
122
|
+
/** True for loopback and private hosts alike. */
|
|
123
|
+
export function isPrivateHost(hostname) {
|
|
124
|
+
return classifyHost(hostname) !== "public";
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Why connecta must not fetch `target`, a URL the downstream advertised, for a
|
|
128
|
+
* connector configured at `configured` — or undefined when it may.
|
|
129
|
+
*
|
|
130
|
+
* - The configured origin is trusted outright: the operator chose it.
|
|
131
|
+
* - Anything else must be `https` on a host that is not private by syntax.
|
|
132
|
+
* - A loopback-configured connector (local development) may also learn
|
|
133
|
+
* loopback URLs, over `http` too, so a local MCP server with a local
|
|
134
|
+
* authorization server keeps working. It still may not learn a LAN or
|
|
135
|
+
* link-local address.
|
|
136
|
+
*
|
|
137
|
+
* The reason names only the host, never the path, query, or credentials a
|
|
138
|
+
* learned URL may carry.
|
|
139
|
+
*/
|
|
140
|
+
export function learnedUrlRefusal(configured, target) {
|
|
141
|
+
if (target.protocol !== "https:" && target.protocol !== "http:") {
|
|
142
|
+
return `${target.host || "the target"} uses ${target.protocol}, not HTTPS`;
|
|
143
|
+
}
|
|
144
|
+
if (target.origin === configured.origin)
|
|
145
|
+
return undefined;
|
|
146
|
+
const targetClass = classifyHost(target.hostname);
|
|
147
|
+
if (targetClass === "loopback" &&
|
|
148
|
+
classifyHost(configured.hostname) === "loopback") {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
if (targetClass !== "public") {
|
|
152
|
+
return `${target.host} is a private, loopback, or link-local address`;
|
|
153
|
+
}
|
|
154
|
+
if (target.protocol !== "https:")
|
|
155
|
+
return `${target.host} is not HTTPS`;
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED