@zackbart/connecta 0.24.4 → 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 +40 -10
- package/CHANGELOG.md +253 -0
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- 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 +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +317 -241
- 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/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 +10 -1
- package/dist/index.js +51 -20
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +206 -92
- 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 +38 -2
- package/dist/program-source.js +205 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +305 -260
- 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 +27 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +50 -166
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +184 -10
- package/documentation/auth.md +80 -0
- package/documentation/code-mode.md +69 -24
- package/documentation/meta-tools.md +164 -27
- package/ethos.md +44 -47
- package/examples/worker/README.md +51 -5
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +6 -6
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -3
- package/templates/node/package.json +1 -1
- 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 -96
- 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 -188
- 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
|
@@ -20,5 +20,14 @@ export interface CredentialVault {
|
|
|
20
20
|
set(connectorId: string, value: string, updatedBy: string, owner?: string): Promise<CredentialMetadata>;
|
|
21
21
|
setAll(connectorId: string, values: ConnectorCredentialValues, updatedBy: string, owner?: string): Promise<CredentialMetadata>;
|
|
22
22
|
delete(connectorId: string, owner?: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Encrypt downstream OAuth state (tokens, a registered client, a PKCE
|
|
25
|
+
* verifier) before it enters storage. `purpose` is the storage key the value
|
|
26
|
+
* is written under, so the result is bound to one connector, owner, and key.
|
|
27
|
+
* Without both `seal` and `open`, that state is stored as plaintext.
|
|
28
|
+
*/
|
|
29
|
+
seal?(connectorId: string, purpose: string, plaintext: string, owner?: string): Promise<string>;
|
|
30
|
+
/** Reverse `seal` for the same connector, purpose, and owner; rejects otherwise. */
|
|
31
|
+
open?(connectorId: string, purpose: string, sealed: string, owner?: string): Promise<string>;
|
|
23
32
|
}
|
|
24
33
|
export {};
|
package/dist/credential-rules.js
CHANGED
|
@@ -3,7 +3,7 @@ export const STORED_CREDENTIAL_SHAPE_MISMATCH_ERROR = "Stored credential fields
|
|
|
3
3
|
/**
|
|
4
4
|
* Compare a connector's current declaration with the keys in its stored
|
|
5
5
|
* credential. Values are deliberately ignored: callers may pass decrypted
|
|
6
|
-
* values or
|
|
6
|
+
* values or the operator UI's masked credential field metadata and get the same answer.
|
|
7
7
|
*
|
|
8
8
|
* The test is CONTAINMENT, not equality: the stored key set is compatible when
|
|
9
9
|
* it holds every field currently declared — the reserved `value` key for a
|
|
@@ -65,7 +65,7 @@ export function describeUndeclaredCredentialFields(fields) {
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* The one rule deciding whether a connector's credential can be tested — read
|
|
68
|
-
* by
|
|
68
|
+
* by the operator UI's credential `testable` flag, by `POST /ui/credentials/<id>/test` when
|
|
69
69
|
* it picks a hook, and by the construction-time mismatch warning, so those
|
|
70
70
|
* three cannot drift apart.
|
|
71
71
|
*
|
package/dist/credentials.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { ConnectorCredentialValues, KVStorage } from "./types.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Encrypted, connector-scoped credential vault over the deployment's existing
|
|
5
5
|
* KVStorage. Only ciphertext enters KV; the AES-GCM key remains an environment
|
|
6
|
-
* secret outside the store.
|
|
6
|
+
* secret outside the store. The same key seals downstream OAuth state, which
|
|
7
|
+
* the OAuth provider stores itself.
|
|
7
8
|
*/
|
|
8
9
|
export declare class CredentialVault implements Vault {
|
|
9
10
|
private readonly storage;
|
|
@@ -17,5 +18,9 @@ export declare class CredentialVault implements Vault {
|
|
|
17
18
|
set(connectorId: string, value: string, updatedBy: string, owner?: string): Promise<CredentialMetadata>;
|
|
18
19
|
setAll(connectorId: string, values: ConnectorCredentialValues, updatedBy: string, owner?: string): Promise<CredentialMetadata>;
|
|
19
20
|
delete(connectorId: string, owner?: string): Promise<void>;
|
|
21
|
+
/** Encrypt downstream OAuth state under the vault key; nothing is stored. */
|
|
22
|
+
seal(connectorId: string, purpose: string, plaintext: string, owner?: string): Promise<string>;
|
|
23
|
+
/** Decrypt what `seal` produced for this connector, purpose, and owner. */
|
|
24
|
+
open(connectorId: string, purpose: string, sealed: string, owner?: string): Promise<string>;
|
|
20
25
|
}
|
|
21
26
|
export declare function encryptedCredentialVault(storage: KVStorage, encryptionKey: string): Vault;
|
package/dist/credentials.js
CHANGED
|
@@ -3,6 +3,16 @@ const IV_BYTES = 12;
|
|
|
3
3
|
const MAX_CREDENTIAL_BYTES = 16_384;
|
|
4
4
|
const encoder = new TextEncoder();
|
|
5
5
|
const decoder = new TextDecoder();
|
|
6
|
+
/** Version tag of the compact `seal` output: `v1.<iv>.<ciphertext>`. */
|
|
7
|
+
const SEALED_VERSION = "v1";
|
|
8
|
+
/**
|
|
9
|
+
* Binds sealed OAuth state to its connector, owner, and physical storage key
|
|
10
|
+
* (which carries the authorization epoch), so ciphertext copied anywhere else
|
|
11
|
+
* does not open. Connector ids cannot contain `|`, and the owner is a hash.
|
|
12
|
+
*/
|
|
13
|
+
function oauthStateAdditionalData(connectorId, purpose, owner) {
|
|
14
|
+
return encoder.encode(`connecta:oauth-state:v1|${connectorId}|${owner ?? ""}|${purpose}`);
|
|
15
|
+
}
|
|
6
16
|
function storageKey(connectorId, owner) {
|
|
7
17
|
return owner
|
|
8
18
|
? `principal:${owner}:conn:${connectorId}:credential:v1`
|
|
@@ -20,7 +30,7 @@ function base64ToBytes(value) {
|
|
|
20
30
|
binary = atob(value);
|
|
21
31
|
}
|
|
22
32
|
catch {
|
|
23
|
-
throw new Error("
|
|
33
|
+
throw new Error("The credential vault encryption key must be a base64-encoded 32-byte key");
|
|
24
34
|
}
|
|
25
35
|
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
26
36
|
}
|
|
@@ -87,7 +97,8 @@ function validateValues(values) {
|
|
|
87
97
|
/**
|
|
88
98
|
* Encrypted, connector-scoped credential vault over the deployment's existing
|
|
89
99
|
* KVStorage. Only ciphertext enters KV; the AES-GCM key remains an environment
|
|
90
|
-
* secret outside the store.
|
|
100
|
+
* secret outside the store. The same key seals downstream OAuth state, which
|
|
101
|
+
* the OAuth provider stores itself.
|
|
91
102
|
*/
|
|
92
103
|
export class CredentialVault {
|
|
93
104
|
storage;
|
|
@@ -96,7 +107,7 @@ export class CredentialVault {
|
|
|
96
107
|
this.storage = storage;
|
|
97
108
|
const raw = base64ToBytes(encryptionKey.trim());
|
|
98
109
|
if (raw.byteLength !== KEY_BYTES) {
|
|
99
|
-
throw new Error("
|
|
110
|
+
throw new Error("The credential vault encryption key must be a base64-encoded 32-byte key");
|
|
100
111
|
}
|
|
101
112
|
this.key = crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
102
113
|
}
|
|
@@ -182,6 +193,37 @@ export class CredentialVault {
|
|
|
182
193
|
async delete(connectorId, owner) {
|
|
183
194
|
await this.storage.delete(storageKey(connectorId, owner));
|
|
184
195
|
}
|
|
196
|
+
/** Encrypt downstream OAuth state under the vault key; nothing is stored. */
|
|
197
|
+
async seal(connectorId, purpose, plaintext, owner) {
|
|
198
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
199
|
+
const ciphertext = await crypto.subtle.encrypt({
|
|
200
|
+
name: "AES-GCM",
|
|
201
|
+
iv,
|
|
202
|
+
additionalData: oauthStateAdditionalData(connectorId, purpose, owner),
|
|
203
|
+
}, await this.key, encoder.encode(plaintext));
|
|
204
|
+
return `${SEALED_VERSION}.${bytesToBase64(iv)}.${bytesToBase64(new Uint8Array(ciphertext))}`;
|
|
205
|
+
}
|
|
206
|
+
/** Decrypt what `seal` produced for this connector, purpose, and owner. */
|
|
207
|
+
async open(connectorId, purpose, sealed, owner) {
|
|
208
|
+
const [version, iv, ciphertext, ...rest] = sealed.split(".");
|
|
209
|
+
if (version !== SEALED_VERSION ||
|
|
210
|
+
iv === undefined ||
|
|
211
|
+
ciphertext === undefined ||
|
|
212
|
+
rest.length > 0) {
|
|
213
|
+
throw new Error("Sealed OAuth state is invalid or corrupted");
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
217
|
+
name: "AES-GCM",
|
|
218
|
+
iv: base64ToBytes(iv),
|
|
219
|
+
additionalData: oauthStateAdditionalData(connectorId, purpose, owner),
|
|
220
|
+
}, await this.key, base64ToBytes(ciphertext));
|
|
221
|
+
return decoder.decode(plaintext);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
throw new Error("Sealed OAuth state could not be decrypted");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
185
227
|
}
|
|
186
228
|
export function encryptedCredentialVault(storage, encryptionKey) {
|
|
187
229
|
return new CredentialVault(storage, encryptionKey);
|