@zackbart/connecta 0.30.0 → 0.31.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/CHANGELOG.md +32 -0
- package/README.md +6 -3
- package/bin/version.mjs +1 -1
- package/dist/call-recovery.js +25 -0
- package/dist/config-schema.d.ts +14 -7
- package/dist/config-schema.js +29 -10
- package/dist/connectors/guarded-fetch.d.ts +9 -1
- package/dist/connectors/guarded-fetch.js +14 -2
- package/dist/errors.d.ts +2 -0
- package/dist/invocation.js +32 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +9 -0
- package/dist/operator-ui/view.js +11 -0
- package/dist/program-writes.js +2 -47
- package/dist/provider.d.ts +7 -5
- package/dist/provider.js +3 -4
- package/dist/providers/_shared/rest/dispatch.js +41 -0
- package/dist/providers/_shared/rest/operation-index.d.ts +133 -0
- package/dist/providers/_shared/rest/operation-index.js +527 -0
- package/dist/providers/_shared/rest/tools.d.ts +141 -0
- package/dist/providers/_shared/rest/tools.js +569 -0
- package/dist/providers/_shared/rest/value-safety.d.ts +151 -0
- package/dist/providers/_shared/rest/value-safety.js +580 -0
- package/dist/providers/cloudflare/graphql.js +385 -0
- package/dist/providers/cloudflare/index.d.ts +47 -51
- package/dist/providers/cloudflare/index.js +188 -1997
- package/dist/providers/cloudflare/openapi.generated.js +3347 -0
- package/dist/providers/cloudflare/rest.d.ts +37 -0
- package/dist/providers/cloudflare/rest.js +1056 -0
- package/dist/providers/cloudflare/skill.generated.js +7 -4
- package/dist/providers/cloudflare/value-safety.js +1247 -0
- package/dist/providers/notion/index.d.ts +35 -31
- package/dist/providers/notion/index.js +60 -1499
- package/dist/providers/notion/openapi.generated.js +75 -0
- package/dist/providers/notion/rest.d.ts +24 -0
- package/dist/providers/notion/rest.js +1067 -0
- package/dist/providers/notion/skill.generated.js +5 -4
- package/dist/providers/notion/value-safety.js +31 -0
- package/dist/providers/stripe/index.d.ts +26 -20
- package/dist/providers/stripe/index.js +103 -130
- package/dist/providers/stripe/openapi.generated.js +649 -0
- package/dist/providers/stripe/rest.d.ts +14 -0
- package/dist/providers/stripe/rest.js +395 -0
- package/dist/providers/stripe/skill.generated.js +7 -5
- package/dist/providers/stripe/value-safety.js +705 -0
- package/dist/providers/vercel/index.d.ts +31 -22
- package/dist/providers/vercel/index.js +96 -853
- package/dist/providers/vercel/openapi.generated.js +456 -0
- package/dist/providers/vercel/rest.d.ts +20 -0
- package/dist/providers/vercel/rest.js +1002 -0
- package/dist/providers/vercel/skill.generated.js +4 -5
- package/dist/providers/vercel/value-safety.js +328 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +47 -0
- package/documentation/README.md +1 -0
- package/documentation/architecture.md +120 -8
- package/documentation/integrating.md +16 -5
- package/documentation/meta-tools.md +9 -2
- package/documentation/provider-auth-migration.md +307 -0
- package/documentation/provider-migration-0.29.md +17 -5
- package/examples/worker/README.md +2 -2
- package/package.json +3 -2
- package/templates/node/README.md +2 -2
- package/templates/node/package.json +1 -1
- package/dist/providers/vercel/mcp-ownership.js +0 -212
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "
|
|
3
|
+
"content": "- Resolve team, project, and deployment ids with list reads; never guess one. Team ids begin `team_`, project ids `prj_`, and deployment ids `dpl_`.\n- Diagnose a deployment by reading its state first (`readyState`, `errorCode`, `errorMessage`), then its build logs, then runtime logs for a deployment that built. Narrow time windows before raising limits.\n- Environment variable changes apply only to future deployments; a new deployment is a separate write. Promoting an existing deployment to production does not rebuild it.\n- Treat every create, update, delete, promotion, cancellation, and purchase as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n",
|
|
4
4
|
"instructionsHeading": "Account instructions",
|
|
5
5
|
"fragments": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"guide_3": "\n\n- Hosted MCP owns discovery, deployments, logs, project-domain listing/adding,\n cancellation and file uploads. Use the separately configured REST complement\n for value-safe project environment variables, uncovered lifecycle/domain\n operations and REST gaps. It has its own connector id and credential.\n- Discover the live catalog before assuming a tool exists. Vercel can change\n the surface independently of a Connecta release, and account features may\n affect what the authorization can reach.\n- Complete authorization and grant access to the owning team before acting. Listing this server does not prove team access. A configured API `teamId` is a routing default, not an authorization boundary.\n- MCP pagination and arguments follow the live MCP schemas; do not apply API pagination or argument shapes to them.\n- Resolve team, project, deployment, run, thread, and order ids with the list\n and get tools. Do not guess opaque ids.\n- Diagnose deployments with `get_deployment`, then build logs, runtime error\n clusters, and runtime logs. Narrow time windows before raising result limits.\n- Purchase tools change billing. Read a quote first and carry its price,\n idempotency key, and requested term into the confirmed purchase unchanged.\n- `get_access_to_vercel_url` creates a temporary access grant. Treat the URL\n it returns as a credential and do not expose it outside the requested task.\n- `deploy_to_vercel` and `import-claude-design-from-url` can create or update\n live projects. Read the target and deployment mode before invoking them.\n- An `auth_required` failure means this connector's OAuth grant is missing or\n expired. Run `authorize_connector` for this connector id, then retry.\n"
|
|
6
|
+
"oauth": "\n\nTool names, descriptions, argument schemas, and result schemas come from Vercel's live server. Connecta applies release-reviewed classification; reviewed writes stay writes even when Vercel claims they only read.\n\n- Discover the live catalog before assuming a tool exists. Vercel can change the surface independently of a Connecta release, and account features may affect what the authorization can reach.\n- Complete authorization and grant access to the owning team before acting. Listing this server does not prove team access.\n- MCP pagination and arguments follow the live MCP schemas; do not apply REST pagination or argument shapes to them.\n- Diagnose deployments with `get_deployment`, then build logs, runtime error clusters, and runtime logs.\n- `get_project_env` and `filter_project_envs` can return decrypted environment values, so Connecta reviews them as writes and the pool's trust policy gates them. Prefer reading variable keys and targets without values.\n- Purchase tools change billing. Read a quote first and carry its price, idempotency key, and requested term into the confirmed purchase unchanged.\n- `get_access_to_vercel_url` creates a temporary access grant. Treat the URL it returns as a credential and do not expose it outside the requested task.\n- `deploy_to_vercel` and `import-claude-design-from-url` can create or update live projects. Read the target and deployment mode before invoking them.\n- An `auth_required` failure means this connector's OAuth grant is missing or expired. Run `authorize_connector` for this connector id, then retry.\n",
|
|
7
|
+
"key": "\n\n- This is Connecta's REST connector to `api.vercel.com`, holding one operator-managed access token. A default team routes calls; it does not narrow what the token can reach.\n- `vercel_api_search` finds API operations, not records: it searches the pinned API index and names the tool that calls each operation. `vercel_api_details` returns one operation's parameters with types, enums, and required names. Neither sends a request.\n- `vercel_api_read` calls GET operations, plus reviewed POSTs that only read: `POST /v2/observability/query`, registrar availability, price, and search, and `POST /v8/artifacts`. `vercel_api_write` calls every other POST, PUT, PATCH, and DELETE, with `method` and `path` stated; execution follows the configured pool trust policy.\n- Paths are concrete and carry the version search returned: `/v13/deployments/dpl_123`, not `/deployments/dpl_123`. Pass `query` and `body` as JSON objects named as `vercel_api_details` lists them. Every call is checked against the pinned index before it is sent: an unknown path returns the nearest operations, and an unknown or missing parameter returns `validation.issues` with the accepted names. Nothing reached Vercel, so fix the call from that answer.\n- Results arrive as `{ status, data, page? }`. On a list, `page.next` is the cursor and `page.param` the query parameter to pass it as: `until` (a millisecond timestamp from `pagination.next`) on most lists, `cursor` on newer ones. Lists belong inside `execute_code`; pass `select` (dot paths such as `deployments.uid` and `deployments.state`) to keep only the fields the question needs.\n- `list_teams` returns team ids and slugs and the connection's default team. `get_deployment_build_logs` reads a deployment's build events without following live output. `get_runtime_logs` collects the runtime log stream for at most `waitMs` or `maxRows`, then stops and says why. Generic reads refuse `follow` and the runtime-log stream, which never end on their own.\n- `vercel_api_upload` sends explicit text or base64 bytes to an octet-stream operation, such as `POST /v2/files` before a deployment (Connecta computes `x-vercel-digest`). `vercel_api_write` sends JSON only.\n- Environment values never come back. `list_project_env_vars` never decrypts. `upsert_project_env_var` (`upsert: false` for create-only), `update_project_env_var`, and `delete_project_env_var` return metadata without values; values are write input only. Generic reads refuse `GET /v1/projects/{idOrName}/env/{id}`, `GET /v10/projects/{idOrName}/env`, `GET /v1/env/{id}`, and any `decrypt`, and Connecta replaces the value of every environment variable in any result with `\"[redacted]\"`, including project reads, shared variables, and audit events.\n- Other secrets read as `[redacted]` in every result: credential-named fields and their whole contents (`token`, `secret`, `password`, `jwt`, `credentials`, `Authorization`, …), protection-bypass secrets (each bypass's metadata stays under a numbered placeholder), deploy hook URLs, drain and webhook headers and signing secrets (including the one a new webhook or drain returns), drain and webhook destination URLs and external route, rewrite, and redirect destinations beyond their scheme and host, the args and values of every header, cookie, and query rule (route transforms and conditions, firewall conditions), team invite codes, synced Global Config items, and credentials inside other URLs. Failures of every reviewed operation, and of any operation whose request takes a credential-named field, say only the status and a reviewed code. Read or rotate withheld secrets in the Vercel dashboard. A `[redacted]` field is never the real value; do not send it back.\n- Refused by design: endpoints that mint credentials (access tokens, AI Gateway API keys, project OIDC and trace tokens, Connect and SSO tokens, Edge Config tokens, KMS-signed tokens, Flags SDK keys, protection-bypass secrets, installation credential rotation), Connect authorization requests, project transfer requests (the code lets another team claim the project), KMS signing, a domain move-out (its answer is a transfer token), Global Config items, backups, and tokens, and domain transfer auth codes. Each refusal names its reason.\n- The hosted OAuth connection's documentation search, runtime error clusters, toolbar threads, agent runs, web analytics summaries, purchase quotes, `deploy_to_vercel`, and access grants for protected URLs do not exist with a token. Configure an OAuth connector for those.\n- A `429` carries Vercel's reset time; Vercel meters endpoints separately, so wait before retrying the same operation. An `auth_required` failure means the token is missing, expired, or outside this team: an operator must replace it in this connection in the operator UI, and `authorize_connector` returns that handoff. A rejected argument comes back in Vercel's own words; read it rather than replacing the token.\n",
|
|
8
|
+
"shared": "- Resolve team, project, and deployment ids with list reads; never guess one. Team ids begin `team_`, project ids `prj_`, and deployment ids `dpl_`.\n- Diagnose a deployment by reading its state first (`readyState`, `errorCode`, `errorMessage`), then its build logs, then runtime logs for a deployment that built. Narrow time windows before raising limits.\n- Environment variable changes apply only to future deployments; a new deployment is a separate write. Promoting an existing deployment to production does not rebuild it.\n- Treat every create, update, delete, promotion, cancellation, and purchase as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n"
|
|
10
9
|
}
|
|
11
10
|
};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// Vercel's reviewed value-safety table (decision 0005, "Value safety"), on the
|
|
2
|
+
// shared mechanism in `../_shared/rest/value-safety.ts`.
|
|
3
|
+
//
|
|
4
|
+
// No tool response, success or error, returns a credential (API, service, or
|
|
5
|
+
// bypass tokens, signing secrets, deploy-hook and other bearer URLs, private
|
|
6
|
+
// keys, authorization verifiers, auth headers) or a stored secret value
|
|
7
|
+
// (environment variables, Global Config items), unless a reviewed named tool
|
|
8
|
+
// exists to return it. Three layers, strongest first:
|
|
9
|
+
//
|
|
10
|
+
// 1. Refuse by operation. `scripts/value-safety.mjs` flags every operation in
|
|
11
|
+
// the pinned spec whose response may carry one (committed as
|
|
12
|
+
// `value-safety.candidates.json`); each needs a verdict here, and
|
|
13
|
+
// `value-safety.node.test.ts` runs the shared harness over this table.
|
|
14
|
+
// 2. Reviewed field paths for `redact` verdicts, applied to every success body
|
|
15
|
+
// on every path (generic tools, HEAD data, named tools, logs, uploads)
|
|
16
|
+
// before cursors or `select` read it. Every reviewed operation, and every
|
|
17
|
+
// one whose request accepts a credential-named field, also answers
|
|
18
|
+
// failures with fixed messages, never the vendor's text.
|
|
19
|
+
// 3. The shared key-name heuristic, defense in depth only, over every body:
|
|
20
|
+
// any subtree under a credential-named key, labelled key/value records
|
|
21
|
+
// whose label is credential-named, environment-variable containers, and
|
|
22
|
+
// URLs with userinfo or credential query parameters.
|
|
23
|
+
//
|
|
24
|
+
// Residual risk, as with Infisical: a secret a person typed into a free-text
|
|
25
|
+
// name, description, log line, or identifier is out of scope.
|
|
26
|
+
import { redact, refuse as refusal, safe, under, } from "../_shared/rest/value-safety.js";
|
|
27
|
+
/** A refusal's reason reads as a sentence about the operation: "It mints …". */
|
|
28
|
+
const refuse = (reason) => refusal(`It ${reason}`);
|
|
29
|
+
/** A project object: env values, deploy hooks, and protection-bypass secrets. */
|
|
30
|
+
const PROJECT = [
|
|
31
|
+
"env[].value",
|
|
32
|
+
"env[].vsmValue",
|
|
33
|
+
"env[].legacyValue",
|
|
34
|
+
"link.deployHooks[].url",
|
|
35
|
+
"protectionBypass@keys",
|
|
36
|
+
// Header, cookie, and query conditions on internal and firewall routes.
|
|
37
|
+
"internalRoutes[].has[?header].value",
|
|
38
|
+
"internalRoutes[].missing[?header].value",
|
|
39
|
+
"security.firewallRoutes[].has[?header].value",
|
|
40
|
+
"security.firewallRoutes[].missing[?header].value",
|
|
41
|
+
];
|
|
42
|
+
/** Project metadata whose names contain a credential word. */
|
|
43
|
+
const PROJECT_ALLOW = [
|
|
44
|
+
"protectionBypass",
|
|
45
|
+
"oidcTokenConfig",
|
|
46
|
+
"security.firewallBypassIps",
|
|
47
|
+
"usageStatus.bypassThrottleUntil",
|
|
48
|
+
"dismissedToasts[].value",
|
|
49
|
+
];
|
|
50
|
+
const DRAIN = [
|
|
51
|
+
"delivery.secret",
|
|
52
|
+
"delivery.headers{}",
|
|
53
|
+
"origin:delivery.endpoint",
|
|
54
|
+
"origin:delivery.endpoint.traces",
|
|
55
|
+
"secret",
|
|
56
|
+
"headers{}",
|
|
57
|
+
"origin:url",
|
|
58
|
+
];
|
|
59
|
+
const SHARED_ENV = ["data[].value", "created[].value", "updated[].value", "failed[].error.value"];
|
|
60
|
+
const PROJECT_ENV = ["value", "legacyValue", "vsmValue", "[].value", "[].legacyValue"];
|
|
61
|
+
const CONNECTOR_ALLOW = ["appTokens", "userTokens"];
|
|
62
|
+
const PROJECT_REASON = "Project objects embed environment values, deploy hook URLs, and protection-bypass secrets.";
|
|
63
|
+
const DRAIN_REASON = "Drains store destination credentials (headers, URLs whose path or query can be a bearer secret) and a signing secret.";
|
|
64
|
+
const WEBHOOK = ["secret", "origin:url"];
|
|
65
|
+
const WEBHOOK_REASON = "Webhooks carry a signing secret and a destination URL whose path can be a bearer secret.";
|
|
66
|
+
/** Route rules: a transform that sets a credential-named header carries the credential in `args`. */
|
|
67
|
+
/**
|
|
68
|
+
* Route rules: every header, cookie, or query transform's `args` and every
|
|
69
|
+
* header, cookie, or query condition's `value` go, whatever the target key's
|
|
70
|
+
* form (a string, or a predicate object such as `{ eq: "Authorization" }`);
|
|
71
|
+
* a header-name list would always miss one (`X-Auth-Key`).
|
|
72
|
+
*/
|
|
73
|
+
const ROUTE_TRANSFORMS = [
|
|
74
|
+
"transforms[].args",
|
|
75
|
+
"has[?header].value",
|
|
76
|
+
"missing[?header].value",
|
|
77
|
+
// Headers a route sets, and the locale cookie it reads (its name is credential vocabulary).
|
|
78
|
+
"headers{}",
|
|
79
|
+
"locale.cookie",
|
|
80
|
+
];
|
|
81
|
+
const DEPLOYMENT = [
|
|
82
|
+
...under(["routes[]", "services[].routes[]", "services[].rewrites[]", "services[].redirects[]"], ROUTE_TRANSFORMS),
|
|
83
|
+
"services[].headers[].headers[].value",
|
|
84
|
+
"services[].headers[].has[?header].value",
|
|
85
|
+
"services[].headers[].missing[?header].value",
|
|
86
|
+
];
|
|
87
|
+
const DEPLOYMENT_REASON = "Deployment routes can set credential-named headers (transforms with target.key Authorization) whose args are the credential.";
|
|
88
|
+
const ROUTE_REASON = "Route transforms can set credential-named headers whose args are the credential.";
|
|
89
|
+
const ENV_REASON = "Environment-variable bodies carry decrypted or encrypted values.";
|
|
90
|
+
const NO_SECRET_REASON = "Answers status or nothing; the flag is the operation's name, not its response.";
|
|
91
|
+
const EDGE_ITEMS = "returns Global Config item values, which may be secrets; read them from your application with the Edge Config SDK.";
|
|
92
|
+
const MINT = "mints a credential and returns it in cleartext.";
|
|
93
|
+
const MODEL_CONFIG = "AI Gateway routing configuration; its descriptions mention tokens as model usage units.";
|
|
94
|
+
const KMS_POLICY = "Issuer policies name environments and claims; signing keys stay with Vercel.";
|
|
95
|
+
/** A firewall rule's header, cookie, and query conditions: their values can be credentials (an `X-Auth-Key` match). */
|
|
96
|
+
const FIREWALL_CONDITIONS = [
|
|
97
|
+
"conditionGroup[].conditions[?header].value",
|
|
98
|
+
"conditionGroup[].conditions[?header].values",
|
|
99
|
+
];
|
|
100
|
+
/** Every firewall config shape: rules, rulesets, and top-level conditions, active, draft, and versions. */
|
|
101
|
+
const FIREWALL = under(["rules[]", "rulesets[]", "conditions[]"], FIREWALL_CONDITIONS);
|
|
102
|
+
const FIREWALL_REASON = "Firewall rules match header, cookie, and query values, which can be credentials a rule checks for (an X-Auth-Key).";
|
|
103
|
+
const TEAM_INVITE = "Teams carry inviteCode, which lets its holder join the team (visible to owners).";
|
|
104
|
+
/**
|
|
105
|
+
* The reviewed verdict for every flagged operation, keyed `METHOD /path`.
|
|
106
|
+
* `refuse` is never sent by the generic tools; named tools that need one of
|
|
107
|
+
* these operations project it themselves (value-safe environment variables).
|
|
108
|
+
*/
|
|
109
|
+
const VALUE_SAFETY = {
|
|
110
|
+
"PATCH /aliases/{id}/protection-bypass": refuse("creates a shareable link that bypasses Deployment Protection."),
|
|
111
|
+
"POST /api-keys": refuse("mints an AI Gateway API key and returns it in cleartext."),
|
|
112
|
+
"POST /v1/connect/authorize/{connector}": refuse("starts a Connect authorization for a caller-chosen subject and returns its verifier and device code."),
|
|
113
|
+
"POST /v1/connect/connectors": safe("Connector capability metadata; appTokens and userTokens describe grants, not tokens.", CONNECTOR_ALLOW),
|
|
114
|
+
"GET /v1/connect/connectors/{connector}": safe("Connector capability metadata.", CONNECTOR_ALLOW),
|
|
115
|
+
"POST /v1/connect/connectors/{connector}/managed/eject": safe("Connector capability metadata.", CONNECTOR_ALLOW),
|
|
116
|
+
"PATCH /v1/connect/connectors/{connector}/trigger-destinations": safe("Connector capability metadata.", CONNECTOR_ALLOW),
|
|
117
|
+
"GET /v2/connect/connectors": safe("Connector capability metadata.", under(["connectors[]"], CONNECTOR_ALLOW)),
|
|
118
|
+
"PATCH /v2/connect/connectors/{connector}": safe("Connector capability metadata.", under(["connector"], CONNECTOR_ALLOW)),
|
|
119
|
+
"POST /v1/connect/token/{connector}": refuse("mints a token for a Connect connector."),
|
|
120
|
+
"GET /v1/drains": redact(DRAIN_REASON, under(["drains[]"], DRAIN)),
|
|
121
|
+
"POST /v1/drains": redact(DRAIN_REASON, DRAIN),
|
|
122
|
+
"POST /v1/drains/test": redact("Answers a delivery test; treated as part of the drain family.", DRAIN),
|
|
123
|
+
"GET /v1/drains/{id}": redact(DRAIN_REASON, DRAIN),
|
|
124
|
+
"PATCH /v1/drains/{id}": redact(DRAIN_REASON, DRAIN),
|
|
125
|
+
"DELETE /v1/drains/{id}": safe(NO_SECRET_REASON),
|
|
126
|
+
"GET /v1/log-drains": redact(DRAIN_REASON, under(["drains[]", "[]"], DRAIN)),
|
|
127
|
+
"POST /v1/log-drains": redact(DRAIN_REASON, DRAIN),
|
|
128
|
+
"GET /v1/log-drains/{id}": redact(DRAIN_REASON, DRAIN),
|
|
129
|
+
"DELETE /v1/log-drains/{id}": safe(NO_SECRET_REASON),
|
|
130
|
+
"GET /v2/integrations/log-drains": redact(DRAIN_REASON, under(["[]"], DRAIN)),
|
|
131
|
+
"POST /v2/integrations/log-drains": redact(DRAIN_REASON, DRAIN),
|
|
132
|
+
"DELETE /v1/integrations/log-drains/{id}": safe(NO_SECRET_REASON),
|
|
133
|
+
"GET /v1/webhooks": redact(WEBHOOK_REASON, under(["[]", "webhooks[]"], WEBHOOK)),
|
|
134
|
+
"POST /v1/webhooks": redact(WEBHOOK_REASON, WEBHOOK),
|
|
135
|
+
"GET /v1/webhooks/{id}": redact(WEBHOOK_REASON, WEBHOOK),
|
|
136
|
+
"DELETE /v1/webhooks/{id}": safe(NO_SECRET_REASON),
|
|
137
|
+
"GET /v1/env": redact(ENV_REASON, SHARED_ENV),
|
|
138
|
+
"POST /v1/env": redact(ENV_REASON, SHARED_ENV),
|
|
139
|
+
"PATCH /v1/env": redact(ENV_REASON, SHARED_ENV),
|
|
140
|
+
"DELETE /v1/env": redact(ENV_REASON, SHARED_ENV),
|
|
141
|
+
"GET /v1/env/{id}": refuse("returns a shared environment variable's decrypted value."),
|
|
142
|
+
"PATCH /v1/env/{id}/unlink/{projectId}": redact(ENV_REASON, ["value"]),
|
|
143
|
+
"GET /v1/global-config": safe("Global Config metadata: ids, slugs, sizes, and digests, without items."),
|
|
144
|
+
"POST /v1/global-config": safe("Answers the created Global Config's metadata."),
|
|
145
|
+
"GET /v1/global-config/{edgeConfigId}": safe("Global Config metadata without items."),
|
|
146
|
+
"PUT /v1/global-config/{edgeConfigId}": safe("Answers the updated Global Config's metadata."),
|
|
147
|
+
"DELETE /v1/global-config/{edgeConfigId}": safe(NO_SECRET_REASON),
|
|
148
|
+
"GET /v1/global-config/{edgeConfigId}/backups": safe("Lists backup versions and sizes, without items."),
|
|
149
|
+
"GET /v1/global-config/{edgeConfigId}/backups/{edgeConfigBackupVersionId}": refuse(EDGE_ITEMS),
|
|
150
|
+
"POST /v1/global-config/{edgeConfigId}/backups/{edgeConfigBackupVersionId}/restore": safe(NO_SECRET_REASON),
|
|
151
|
+
"GET /v1/global-config/{edgeConfigId}/item/{edgeConfigItemKey}": refuse(EDGE_ITEMS),
|
|
152
|
+
"GET /v1/global-config/{edgeConfigId}/items": refuse(EDGE_ITEMS),
|
|
153
|
+
"PATCH /v1/global-config/{edgeConfigId}/items": safe("Answers a status; item values are write input only."),
|
|
154
|
+
"GET /v1/global-config/{edgeConfigId}/schema": safe("A JSON Schema for items, without values."),
|
|
155
|
+
"POST /v1/global-config/{edgeConfigId}/schema": safe("Answers the schema, without values."),
|
|
156
|
+
"DELETE /v1/global-config/{edgeConfigId}/schema": safe(NO_SECRET_REASON),
|
|
157
|
+
"POST /v1/global-config/{edgeConfigId}/token": refuse(MINT),
|
|
158
|
+
"GET /v1/global-config/{edgeConfigId}/token/{token}": refuse("returns a Global Config read token."),
|
|
159
|
+
"GET /v1/global-config/{edgeConfigId}/tokens": refuse("returns every Global Config read token in cleartext."),
|
|
160
|
+
"DELETE /v1/global-config/{edgeConfigId}/tokens": safe(NO_SECRET_REASON),
|
|
161
|
+
"GET /v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/global-config": refuse(EDGE_ITEMS),
|
|
162
|
+
"PUT /v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/global-config": redact("Answers the synced Global Config, items included; item values can be secrets.", ["items"]),
|
|
163
|
+
"HEAD /v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/global-config": safe("Headers only; the heuristic still covers them."),
|
|
164
|
+
"POST /v1/installations/{integrationConfigurationId}/credentials/revoke": safe(NO_SECRET_REASON),
|
|
165
|
+
"POST /v1/installations/{integrationConfigurationId}/credentials/rotate": refuse("rotates an installation credential and returns the new access token."),
|
|
166
|
+
"PUT /v1/installations/{integrationConfigurationId}/resources/{resourceId}/secrets": safe("Answers nothing; secrets are write input only."),
|
|
167
|
+
"POST /v1/integrations/sso/token": refuse("exchanges an integration SSO code for access, ID, and refresh tokens."),
|
|
168
|
+
"POST /v1/kms/issuers/{issuerId}/keys": safe("Answers public key material and key metadata; private keys stay with Vercel."),
|
|
169
|
+
"POST /v1/kms/issuers/{issuerId}/keys/{keyId}/activate": safe("Answers key metadata."),
|
|
170
|
+
"POST /v1/kms/issuers/{issuerId}/keys/{keyId}/revoke": safe("Answers issuer metadata; tokenClaims are policy, not tokens."),
|
|
171
|
+
"POST /v1/kms/issuers/{issuerId}/sign/message": refuse("signs a message with the issuer's private key, acting as the organization."),
|
|
172
|
+
"POST /v1/kms/issuers/{issuerId}/sign/token": refuse("signs a token with the issuer's private key."),
|
|
173
|
+
"GET /v1/microfrontends/groups/{groupId}/projects": redact(PROJECT_REASON, under(["projects[]"], PROJECT), under(["projects[]"], PROJECT_ALLOW)),
|
|
174
|
+
"POST /v1/projects/traces/session": refuse("mints a trace session token for a deployment."),
|
|
175
|
+
"POST /v1/projects/{idOrName}/avatar": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
176
|
+
"DELETE /v1/projects/{idOrName}/avatar": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
177
|
+
"DELETE /v1/projects/{idOrName}/env": redact(ENV_REASON, PROJECT_ENV),
|
|
178
|
+
"GET /v1/projects/{idOrName}/env/{id}": refuse("returns one environment variable's decrypted value. Read its metadata with list_project_env_vars."),
|
|
179
|
+
"PATCH /v1/projects/{idOrName}/protection-bypass": refuse("generates Protection Bypass for Automation secrets."),
|
|
180
|
+
"POST /v1/projects/{idOrName}/token": refuse("mints a project OIDC token."),
|
|
181
|
+
"GET /v1/projects/{projectIdOrName}/feature-flags/sdk-keys": safe("Lists SDK key metadata with masked partialKeyValue."),
|
|
182
|
+
"PUT /v1/projects/{projectIdOrName}/feature-flags/sdk-keys": refuse("mints a Flags SDK key and returns it in cleartext."),
|
|
183
|
+
"DELETE /v1/projects/{projectIdOrName}/feature-flags/sdk-keys/{hashKey}": safe(NO_SECRET_REASON),
|
|
184
|
+
"PATCH /v1/projects/{projectId}/microfrontends": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
185
|
+
"GET /v1/projects/{projectId}/routes": redact(ROUTE_REASON, under(["routes[].route"], ROUTE_TRANSFORMS)),
|
|
186
|
+
"POST /v1/projects/{projectId}/routes": redact(ROUTE_REASON, under(["route.route"], ROUTE_TRANSFORMS)),
|
|
187
|
+
"POST /v1/projects/{projectId}/routes/generate": redact(ROUTE_REASON, [
|
|
188
|
+
...under(["route"], ROUTE_TRANSFORMS),
|
|
189
|
+
"route.actions[].headers[].value",
|
|
190
|
+
"route.conditions[?header].value",
|
|
191
|
+
]),
|
|
192
|
+
"PATCH /v1/projects/{projectId}/routes/{routeId}": redact(ROUTE_REASON, under(["route.route"], ROUTE_TRANSFORMS)),
|
|
193
|
+
"GET /v1/registrar/domains/{domain}/auth-code": refuse("returns the domain's transfer authorization code, which lets its holder move the domain to another registrar."),
|
|
194
|
+
"GET /v1/security/firewall/bypass": safe("IP bypass rules are firewall configuration: addresses, not secrets."),
|
|
195
|
+
"POST /v1/security/firewall/bypass": safe("IP bypass rules are firewall configuration."),
|
|
196
|
+
"DELETE /v1/security/firewall/bypass": safe("IP bypass rules are firewall configuration."),
|
|
197
|
+
"GET /v1/security/firewall/config": redact(FIREWALL_REASON, under(["active", "draft", "versions[]"], FIREWALL)),
|
|
198
|
+
"PUT /v1/security/firewall/config": redact(FIREWALL_REASON, under(["active", "draft", "versions[]"], FIREWALL)),
|
|
199
|
+
"POST /v1/security/firewall/config/generate-rule": redact(FIREWALL_REASON, under(["rule"], FIREWALL_CONDITIONS)),
|
|
200
|
+
"GET /v1/security/firewall/config/{configVersion}": redact(FIREWALL_REASON, FIREWALL),
|
|
201
|
+
"POST /v1/security/firewall/config/{configVersion}/activate": redact(FIREWALL_REASON, FIREWALL),
|
|
202
|
+
"POST /v1/storage/stores/integration/direct": safe("store.secrets lists secret names and lengths, never values.", [
|
|
203
|
+
"store.secrets",
|
|
204
|
+
"store.secretRotationRequestedBy",
|
|
205
|
+
"store.secretRotationRequestedReason",
|
|
206
|
+
]),
|
|
207
|
+
"GET /v1/vcr/repository/{idOrName}/images/{imageIdOrDigest}": redact("Image layer environment entries are build-time NAME=value pairs.", ["image.layers[].env"]),
|
|
208
|
+
"GET /v10/projects": redact(PROJECT_REASON, under(["projects[]", "[]"], PROJECT), under(["projects[]", "[]"], PROJECT_ALLOW)),
|
|
209
|
+
"GET /v10/projects/{idOrName}/env": refuse("returns environment values beside their metadata. Use list_project_env_vars, which never decrypts or returns a value."),
|
|
210
|
+
"POST /v10/projects/{idOrName}/env": redact(ENV_REASON, [
|
|
211
|
+
"created.value",
|
|
212
|
+
"created.vsmValue",
|
|
213
|
+
"created.legacyValue",
|
|
214
|
+
"created[].value",
|
|
215
|
+
"created[].vsmValue",
|
|
216
|
+
"created[].legacyValue",
|
|
217
|
+
"failed[].error.value",
|
|
218
|
+
]),
|
|
219
|
+
"POST /v11/projects": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
220
|
+
"PATCH /v12/deployments/{id}/cancel": redact(DEPLOYMENT_REASON, DEPLOYMENT),
|
|
221
|
+
"POST /v13/deployments": redact(DEPLOYMENT_REASON, DEPLOYMENT),
|
|
222
|
+
"GET /v13/deployments/{idOrUrl}": redact(DEPLOYMENT_REASON, DEPLOYMENT),
|
|
223
|
+
"GET /v2/deployments/{id}/aliases": redact("Aliases carry protection-bypass secrets as map keys.", ["aliases[].protectionBypass@keys"], ["aliases[].protectionBypass"]),
|
|
224
|
+
"POST /v2/deployments/{id}/aliases": safe("Answers the alias assignment."),
|
|
225
|
+
"GET /v2/user": safe("ipBypass lists firewall IP rules, not secrets.", ["user.resourceConfig.security.ipBypass"]),
|
|
226
|
+
"PATCH /v3/domains/{domain}": redact("A move-out answers a transfer token; Connecta refuses move-out by body, and redacts any token.", ["token"]),
|
|
227
|
+
"GET /v3/events": redact("Audit events carry environment variables (with or without key or type) and credential metadata in payloads.", [
|
|
228
|
+
"events[].payload.newEnvVar.value",
|
|
229
|
+
"events[].payload.oldEnvVar.value",
|
|
230
|
+
"events[?env].payload.value",
|
|
231
|
+
"events[?env].payload.vsmValue",
|
|
232
|
+
"events[].payload.apiKey",
|
|
233
|
+
"events[].payload.credential",
|
|
234
|
+
"events[].payload.inviteCode",
|
|
235
|
+
"events[].payload.gitCredentialSource",
|
|
236
|
+
]),
|
|
237
|
+
"POST /v3/user/tokens": refuse("mints a Vercel access token."),
|
|
238
|
+
"DELETE /v3/user/tokens/{tokenId}": safe(NO_SECRET_REASON),
|
|
239
|
+
"GET /v4/aliases": redact("Aliases carry protection-bypass secrets as map keys.", ["aliases[].protectionBypass@keys"], ["aliases[].protectionBypass"]),
|
|
240
|
+
"GET /v4/aliases/{idOrAlias}": redact("Aliases carry protection-bypass secrets as map keys.", ["protectionBypass@keys"], ["protectionBypass"]),
|
|
241
|
+
"GET /v5/user/tokens/{tokenId}": safe("Answers token metadata (`token` is an object of ids, prefix, suffix, and scopes).", ["token"]),
|
|
242
|
+
"GET /v6/user/tokens": safe("Lists token metadata, never token values.", ["tokens"]),
|
|
243
|
+
"GET /v9/projects/{idOrName}": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
244
|
+
"PATCH /v9/projects/{idOrName}": redact(PROJECT_REASON, PROJECT, PROJECT_ALLOW),
|
|
245
|
+
"PATCH /v9/projects/{idOrName}/env/{id}": redact(ENV_REASON, PROJECT_ENV),
|
|
246
|
+
"DELETE /v9/projects/{idOrName}/env/{id}": redact(ENV_REASON, PROJECT_ENV),
|
|
247
|
+
// Flagged by description or a transfer/claim/invite family (round 3).
|
|
248
|
+
"POST /projects/{idOrName}/transfer-request": refuse("returns a code that lets another team claim the project for 24 hours."),
|
|
249
|
+
"PUT /projects/transfer-request/{code}": safe("Accepts a transfer with a code the caller holds; answers transfer results."),
|
|
250
|
+
"GET /ai-gateway/virtual-model-configs": safe(MODEL_CONFIG),
|
|
251
|
+
"POST /ai-gateway/virtual-model-configs": safe(MODEL_CONFIG),
|
|
252
|
+
"PATCH /ai-gateway/virtual-model-configs": safe(MODEL_CONFIG),
|
|
253
|
+
"GET /ai-gateway/virtual-model-configs/list": safe(MODEL_CONFIG),
|
|
254
|
+
"GET /ai-gateway/virtual-model-configs/{vmcSlug}": safe(MODEL_CONFIG),
|
|
255
|
+
"PATCH /ai-gateway/virtual-model-configs/{vmcSlug}": safe(MODEL_CONFIG),
|
|
256
|
+
"GET /v1/ai-gateway/routers": safe(MODEL_CONFIG),
|
|
257
|
+
"POST /storage/stores/blob": safe("store.kind names the store type; no token is returned."),
|
|
258
|
+
"GET /storage/stores/{id}": safe("A store's metadata; projectsMetadata lists environment variable names, not values."),
|
|
259
|
+
"GET /v1/installations/{integrationConfigurationId}/resources/{resourceId}": safe("customClaims are OIDC claim configuration, not tokens."),
|
|
260
|
+
"GET /v1/integrations/configuration/{id}/products": redact("protocols.authentication describes supported auth flows, not credentials; a product's log and trace drain protocols carry destination headers.", [
|
|
261
|
+
"products[].protocols.logDrain.headers{}",
|
|
262
|
+
"products[].protocols.traceDrain.headers{}",
|
|
263
|
+
"origin:products[].protocols.logDrain.endpoint",
|
|
264
|
+
"origin:products[].protocols.traceDrain.endpoint",
|
|
265
|
+
]),
|
|
266
|
+
"GET /v1/kms/issuers": safe(KMS_POLICY),
|
|
267
|
+
"POST /v1/kms/issuers": safe(KMS_POLICY),
|
|
268
|
+
"GET /v1/kms/issuers/{issuerId}": safe(KMS_POLICY),
|
|
269
|
+
"PATCH /v1/kms/issuers/{issuerId}": safe(KMS_POLICY),
|
|
270
|
+
"POST /v1/kms/issuers/{issuerId}/policies": safe(KMS_POLICY),
|
|
271
|
+
"PATCH /v1/kms/issuers/{issuerId}/policies/{kind}/{policyKey}": safe(KMS_POLICY),
|
|
272
|
+
"GET /v1/registrar/domains/{domain}/transfer": safe("Answers a transfer-in status only."),
|
|
273
|
+
"POST /v1/registrar/domains/{domain}/transfer": safe("Answers an order id; the auth code is request input only."),
|
|
274
|
+
"DELETE /v1/teams/{teamId}/invites/{inviteId}": safe(NO_SECRET_REASON),
|
|
275
|
+
"GET /v2/teams": redact(TEAM_INVITE, ["teams[].inviteCode"]),
|
|
276
|
+
"GET /v2/teams/{teamId}": redact(TEAM_INVITE, ["inviteCode"]),
|
|
277
|
+
"PATCH /v2/teams/{teamId}": redact(TEAM_INVITE, ["inviteCode"]),
|
|
278
|
+
"GET /v3/teams/{teamId}/members": safe("emailInviteCodes lists pending invitations (email, role, expiry) without codes.", ["emailInviteCodes"]),
|
|
279
|
+
"POST /v2/teams/{teamId}/members": safe("Answers the invited email, role, and permissions; no invite link or code."),
|
|
280
|
+
"POST /v7/domains": safe("Answers the added domain's metadata."),
|
|
281
|
+
"POST /v9/domains/{domain}/claim": safe("Claims by verified TXT record and answers domain metadata; issues nothing."),
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Vercel's reviewed table. Environment-variable endpoints answer bodies that
|
|
285
|
+
* are environment variables whether or not each carries a `key`, so their
|
|
286
|
+
* values go whole; `permissions` maps list action scopes under resource
|
|
287
|
+
* names, which the heuristic leaves.
|
|
288
|
+
*/
|
|
289
|
+
export const VERCEL_VALUE_SAFETY = {
|
|
290
|
+
title: "Vercel",
|
|
291
|
+
operations: VALUE_SAFETY,
|
|
292
|
+
envBodies: /^\/v\d+\/(?:env(?:\/|$)|projects\/\{[^}]+\}\/env(?:\/|$))/,
|
|
293
|
+
scopeMaps: ["permissions"],
|
|
294
|
+
fields: Object.fromEntries([
|
|
295
|
+
["env", "Environment names and variable-name lists; values inside environment containers go structurally."],
|
|
296
|
+
["environments", "Environment slugs or ids a policy or variable applies to."],
|
|
297
|
+
["environmentVariables", "Environment variable names a store connects, never values."],
|
|
298
|
+
["type", "A variable's or record's type (plain, encrypted, sensitive, secret)."],
|
|
299
|
+
["visibility", "A variable's visibility setting."],
|
|
300
|
+
["keys", "Rate-limit key kinds (ip, ja4) and changed variable names."],
|
|
301
|
+
["logHeaders", "Header names a firewall rule logs."],
|
|
302
|
+
["log_headers", "Header names the firewall logs."],
|
|
303
|
+
["signingKeys", "KMS signing key metadata and public key material; private keys stay with Vercel."],
|
|
304
|
+
["bindings", "Service-to-service binding names and targets."],
|
|
305
|
+
["canaryResponseHeader", "Whether a canary response sets a header; a boolean."],
|
|
306
|
+
["inferenceRegion", "An AI Gateway routing region."],
|
|
307
|
+
["providerTimeouts", "Provider timeouts in milliseconds."],
|
|
308
|
+
["redirectUri", "A connector's registered OAuth redirect URI, public by design."],
|
|
309
|
+
["supportsRevocation", "Whether a connector can revoke tokens; a boolean."],
|
|
310
|
+
["claims", "OIDC claim matchers a policy requires, not tokens."],
|
|
311
|
+
["customClaims", "OIDC claim configuration, not tokens."],
|
|
312
|
+
["limited", "Whether a record is limited by the token's privileges; a boolean."],
|
|
313
|
+
["enforced", "Whether SAML is enforced; a boolean."],
|
|
314
|
+
["kind", "A store or record kind."],
|
|
315
|
+
["budget", "An AI Gateway key's spend budget."],
|
|
316
|
+
["origin", "How a token was issued (manual, oauth)."],
|
|
317
|
+
["scope", "A token's scope (user, team, project)."],
|
|
318
|
+
["issuedBefore", "A revocation cutoff timestamp."],
|
|
319
|
+
["issuerUrl", "The OIDC issuer URL, public by design."],
|
|
320
|
+
["oidcSubject", "The OIDC subject claim, an identifier."],
|
|
321
|
+
["next", "A pagination cursor; it grants nothing."],
|
|
322
|
+
["partialKeyValue", "A masked SDK key preview."],
|
|
323
|
+
["appUrlRegistrationSupport", "Whether a partner accepts app URLs; a boolean."],
|
|
324
|
+
["disjunctiveProductionSecretPolicy", "A team policy setting."],
|
|
325
|
+
["sensitiveEnvironmentVariablePolicy", "A team policy setting."],
|
|
326
|
+
["strictPasswordProtectionSettings", "A team policy setting."],
|
|
327
|
+
].map(([name, reason]) => [name, { verdict: "keep", reason }])),
|
|
328
|
+
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CONNECTA_VERSION = "0.
|
|
1
|
+
export declare const CONNECTA_VERSION = "0.31.0";
|
package/dist/version.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a write landed, as far as anyone can know.
|
|
3
|
+
*
|
|
4
|
+
* `unknown` is the outcome a result must never paper over: the call left
|
|
5
|
+
* connecta and no answer came back, so sending it again could do it twice and
|
|
6
|
+
* not sending it could leave it undone. That is a dispatched call that timed
|
|
7
|
+
* out, was cancelled, found the service unavailable, or failed any way that
|
|
8
|
+
* is not a verdict. A refusal code (`REFUSALS`) or a downstream tool's own
|
|
9
|
+
* `isError` is an answer, so it is `failed`. Response text does not decide
|
|
10
|
+
* whether an outcome is unknown. A call that was never dispatched is
|
|
11
|
+
* `failed`: nothing was sent.
|
|
12
|
+
* `result_processing_failed` means the downstream call completed.
|
|
13
|
+
*/
|
|
14
|
+
export function classifyWriteOutcome(outcome) {
|
|
15
|
+
if (outcome.ok)
|
|
16
|
+
return "ok";
|
|
17
|
+
if (!outcome.dispatched)
|
|
18
|
+
return "failed";
|
|
19
|
+
const code = outcome.error?.code;
|
|
20
|
+
if (code === "result_processing_failed")
|
|
21
|
+
return "ok";
|
|
22
|
+
if (code === "timeout" || code === "cancelled" || code === "unavailable") {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
if (code !== undefined && REFUSALS.has(code))
|
|
26
|
+
return "failed";
|
|
27
|
+
return outcome.answered === true ? "failed" : "unknown";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Codes that say the other side refused the call rather than acted on it:
|
|
31
|
+
* the credential, the arguments, the resource, the rate, or a base version
|
|
32
|
+
* someone else already moved past (`conflict`). Anything else a
|
|
33
|
+
* connector reports after dispatch — `connector_call_failed` from a response
|
|
34
|
+
* too large to read, a redirect it would not follow, a body it could not
|
|
35
|
+
* parse, a 5xx — may come after the write landed, so only a downstream
|
|
36
|
+
* tool's own `isError` answer makes it a known failure.
|
|
37
|
+
*/
|
|
38
|
+
const REFUSALS = new Set([
|
|
39
|
+
"auth_required",
|
|
40
|
+
"downstream_oauth_required",
|
|
41
|
+
"provider_permission_denied",
|
|
42
|
+
"invalid_args",
|
|
43
|
+
"not_found",
|
|
44
|
+
"rate_limited",
|
|
45
|
+
"input_required_unsupported",
|
|
46
|
+
"conflict",
|
|
47
|
+
]);
|
package/documentation/README.md
CHANGED
|
@@ -22,6 +22,7 @@ service, or change Connecta itself.
|
|
|
22
22
|
| [Operator UI](./operator-ui.md) | Optional UI setup, scoped configuration and live-data contract, activity disclosure, and UI implementation |
|
|
23
23
|
| [Operator UI tests](./operator-tests.md) | Browser state matrix, real-server flows, and snapshot maintenance |
|
|
24
24
|
| [Provider migration for 0.29](./provider-migration-0.29.md) | Upgrade actions for Notion, Vercel, and Cloudflare; provider reconciliation tables supply exact name mappings |
|
|
25
|
+
| [Provider auth migration](./provider-auth-migration.md) | Upgrade actions when `auth` selects hosted MCP or Connecta's REST connector, Stripe and Vercel |
|
|
25
26
|
|
|
26
27
|
Each long contract starts with task routes. Follow those section links before
|
|
27
28
|
reading the whole guide. Source and test pointers identify where to check a
|