@outcrawl/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -0
- package/dist/index.js +2493 -0
- package/dist/types/_deps/core/agent-alias.d.ts +219 -0
- package/dist/types/_deps/core/brand.d.ts +62 -0
- package/dist/types/_deps/core/certificate.d.ts +100 -0
- package/dist/types/_deps/core/cron.d.ts +116 -0
- package/dist/types/_deps/core/errors.d.ts +340 -0
- package/dist/types/_deps/core/index.d.ts +17 -0
- package/dist/types/_deps/core/money.d.ts +108 -0
- package/dist/types/_deps/core/registry.d.ts +466 -0
- package/dist/types/_deps/core/rules.d.ts +237 -0
- package/dist/types/_deps/core/secrets.d.ts +278 -0
- package/dist/types/_deps/core/types.d.ts +1677 -0
- package/dist/types/_deps/integrations/connector.d.ts +153 -0
- package/dist/types/_deps/replay/events.d.ts +1004 -0
- package/dist/types/agent.d.ts +313 -0
- package/dist/types/availability.d.ts +86 -0
- package/dist/types/browser.d.ts +107 -0
- package/dist/types/client.d.ts +128 -0
- package/dist/types/hands.d.ts +259 -0
- package/dist/types/index.d.ts +111 -0
- package/dist/types/integrations.d.ts +63 -0
- package/dist/types/monitors.d.ts +24 -0
- package/dist/types/page.d.ts +96 -0
- package/dist/types/profiles.d.ts +26 -0
- package/dist/types/result.d.ts +90 -0
- package/dist/types/rules.d.ts +41 -0
- package/dist/types/scrape.d.ts +68 -0
- package/dist/types/secrets.d.ts +36 -0
- package/dist/types/sessions.d.ts +124 -0
- package/dist/types/transport.d.ts +250 -0
- package/package.json +70 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connector registry: what a workspace has connected, and what it cannot.
|
|
3
|
+
*
|
|
4
|
+
* Outcrawl is an MCP **server** inbound — `packages/mcp` — and this package is
|
|
5
|
+
* the outbound half. A workspace connects Gmail, Slack or Twilio and a run
|
|
6
|
+
* calls those tools mid-task. The two directions share nothing but the
|
|
7
|
+
* protocol; the inbound server is untouched by any of this.
|
|
8
|
+
*
|
|
9
|
+
* This module holds the row and the rules that decide whether a row may exist
|
|
10
|
+
* at all. The rules are the security surface of the whole feature, so they are
|
|
11
|
+
* here, in one place, with their reasoning, rather than distributed across a
|
|
12
|
+
* handler and a client.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* How we reach a connected MCP server. **HTTPS, and there is no second arm.**
|
|
16
|
+
*
|
|
17
|
+
* ## Why stdio is refused, and why the refusal is a union arm and not an omission
|
|
18
|
+
*
|
|
19
|
+
* The MCP ecosystem's default transport is stdio: a client spawns
|
|
20
|
+
* `npx -y @modelcontextprotocol/server-gmail` and speaks JSON-RPC over its
|
|
21
|
+
* pipes. Supporting that here would mean `POST /v1/integrations` accepts a
|
|
22
|
+
* command line and a fleet Mac executes it.
|
|
23
|
+
*
|
|
24
|
+
* A fleet Mac is multi-tenant. It holds `TICKET_SECRET`, profile blobs, and
|
|
25
|
+
* other accounts' live browser contexts, and there is no sandbox between a
|
|
26
|
+
* spawned child process and any of that. A `stdio` field on this type is
|
|
27
|
+
* therefore a JSON-schema'd remote code execution endpoint running as the
|
|
28
|
+
* worker user, reachable by anyone with an API key.
|
|
29
|
+
*
|
|
30
|
+
* An allowlist does not fix it. The canonical invocation is `npx -y <package>`,
|
|
31
|
+
* which is `npx` fetching arbitrary code off the network at call time — **an
|
|
32
|
+
* allowlist of names, not of code.** Pinning versions moves the trust to a
|
|
33
|
+
* registry we do not control; vendoring the servers means shipping a Gmail
|
|
34
|
+
* client in our worker bundle and calling it an integration.
|
|
35
|
+
*
|
|
36
|
+
* And it buys nothing we need. Gmail, Slack and Twilio — the three the product
|
|
37
|
+
* was scoped around — all publish hosted HTTP MCP endpoints. Every use case
|
|
38
|
+
* the feature exists for is served without ever spawning a process.
|
|
39
|
+
*
|
|
40
|
+
* So this is a discriminated union with exactly one arm. That is the same
|
|
41
|
+
* discipline `mcpTool: string | null` uses in the capability registry: a
|
|
42
|
+
* declined option must not be spellable the same way as a forgotten one.
|
|
43
|
+
* Adding a `stdio` arm later — when there is a per-run sandbox to run it in —
|
|
44
|
+
* is a compile error at every `switch` in this package until somebody has
|
|
45
|
+
* handled it deliberately.
|
|
46
|
+
*/
|
|
47
|
+
export type ConnectorTransport = {
|
|
48
|
+
readonly kind: 'http';
|
|
49
|
+
/** Absolute `https://` endpoint speaking MCP over HTTP. */
|
|
50
|
+
readonly url: string;
|
|
51
|
+
};
|
|
52
|
+
export declare const CONNECTOR_TRANSPORT_KINDS: readonly ["http"];
|
|
53
|
+
/**
|
|
54
|
+
* How we authenticate to the remote server — **by handle, never by value.**
|
|
55
|
+
*
|
|
56
|
+
* `secret` is a handle into the workspace secrets store (`@outcrawl/core`'s
|
|
57
|
+
* `secrets.*` capabilities, bare string, `^[a-z0-9][a-z0-9_-]{0,63}$`). It is
|
|
58
|
+
* what the row stores, what `integrations.list` returns and what appears in
|
|
59
|
+
* any log. The plaintext is resolved at the instant of a call, by the tier
|
|
60
|
+
* that makes the call, and nothing here ever holds one.
|
|
61
|
+
*
|
|
62
|
+
* There is no second credential store. A connector that wanted its own would
|
|
63
|
+
* be a second place a customer's Gmail token lives, with its own encryption
|
|
64
|
+
* decision, its own rotation story and its own bugs.
|
|
65
|
+
*/
|
|
66
|
+
export type ConnectorAuth = {
|
|
67
|
+
readonly kind: 'none';
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: 'bearer';
|
|
70
|
+
readonly secret: string;
|
|
71
|
+
} | {
|
|
72
|
+
readonly kind: 'header';
|
|
73
|
+
readonly header: string;
|
|
74
|
+
readonly secret: string;
|
|
75
|
+
};
|
|
76
|
+
export declare const CONNECTOR_AUTH_KINDS: readonly ["none", "bearer", "header"];
|
|
77
|
+
/**
|
|
78
|
+
* THE ROW IS THE RESPONSE. `integrations.list` answers these verbatim and
|
|
79
|
+
* there is no projection in front of it, unlike `secrets.list`, which has to
|
|
80
|
+
* withhold a ciphertext.
|
|
81
|
+
*
|
|
82
|
+
* That is not a shortcut, it is the property: a connector row holds a secrets
|
|
83
|
+
* store HANDLE and never a value, so there is nothing on it to withhold. A
|
|
84
|
+
* `ConnectorView` that stripped fields would imply the row has some — and the
|
|
85
|
+
* day somebody adds a field worth hiding, they will add it to a type nobody
|
|
86
|
+
* reads twice. Making the row publishable in full is what keeps it honest.
|
|
87
|
+
*/
|
|
88
|
+
export interface Connector {
|
|
89
|
+
readonly id: string;
|
|
90
|
+
/**
|
|
91
|
+
* The name the model addresses this connector by, and the left half of every
|
|
92
|
+
* `<connector>.<tool>` the prompt shows. Lowercase, stable, unique per
|
|
93
|
+
* account: `gmail`, `slack`, `twilio`.
|
|
94
|
+
*/
|
|
95
|
+
readonly name: string;
|
|
96
|
+
readonly transport: ConnectorTransport;
|
|
97
|
+
readonly auth: ConnectorAuth;
|
|
98
|
+
/**
|
|
99
|
+
* The tools this connector may expose, or `null` for every tool the remote
|
|
100
|
+
* advertises.
|
|
101
|
+
*
|
|
102
|
+
* Required-and-nullable rather than optional, on the registry's own rule: "I
|
|
103
|
+
* reviewed the catalogue and all of it is fine" and "nobody said" are
|
|
104
|
+
* different facts and must not be the same bytes. This is the field a
|
|
105
|
+
* customer reads to answer *what can an agent reach outside the browser*,
|
|
106
|
+
* and an undefined that renders as "everything" is the one answer nobody
|
|
107
|
+
* consented to.
|
|
108
|
+
*/
|
|
109
|
+
readonly tools: readonly string[] | null;
|
|
110
|
+
readonly createdAt: string;
|
|
111
|
+
}
|
|
112
|
+
/** A connector on its way in. No id and no timestamp: both are the store's. */
|
|
113
|
+
export interface ConnectorInput {
|
|
114
|
+
readonly name: string;
|
|
115
|
+
readonly transport: ConnectorTransport;
|
|
116
|
+
readonly auth: ConnectorAuth;
|
|
117
|
+
readonly tools: readonly string[] | null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Whether a hostname is an IP literal in a range that is not on the public
|
|
121
|
+
* internet.
|
|
122
|
+
*
|
|
123
|
+
* ## What this closes, and — as importantly — what it does not
|
|
124
|
+
*
|
|
125
|
+
* It closes the direct attack: `POST /v1/integrations` with
|
|
126
|
+
* `https://169.254.169.254/latest/meta-data/` or `https://10.0.0.5/`, which
|
|
127
|
+
* would otherwise make our own egress into a reader for whatever is on the
|
|
128
|
+
* other side. That is worth closing on its own and it is cheap.
|
|
129
|
+
*
|
|
130
|
+
* It does NOT close DNS rebinding — a name that resolves publicly when this
|
|
131
|
+
* runs and privately when the call is made. Closing that needs the resolved
|
|
132
|
+
* address at connect time and at call time, and the tier that makes the call
|
|
133
|
+
* is a Cloudflare Worker, whose runtime exposes no resolver and no socket. So
|
|
134
|
+
* this is stated as a partial defence rather than asserted as a complete one.
|
|
135
|
+
*
|
|
136
|
+
* The residual risk is bounded by WHERE the call runs, which is the second
|
|
137
|
+
* reason the tool call is proxied through the edge rather than made from a
|
|
138
|
+
* fleet Mac: a Mac sits on a private network with the rest of the fleet on it,
|
|
139
|
+
* and the Worker does not.
|
|
140
|
+
*/
|
|
141
|
+
export declare function isPrivateAddressLiteral(host: string): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* The one rule for "an endpoint this product will make an authenticated
|
|
144
|
+
* outbound request to". Throws `BadRequestError` naming the field.
|
|
145
|
+
*/
|
|
146
|
+
export declare function connectorUrl(value: unknown): string;
|
|
147
|
+
/**
|
|
148
|
+
* Read a connector off a request body. Throws `BadRequestError` naming the
|
|
149
|
+
* field, which is what the API surfaces as a 400.
|
|
150
|
+
*/
|
|
151
|
+
export declare function parseConnectorInput(body: Readonly<Record<string, unknown>>): ConnectorInput;
|
|
152
|
+
/** Every field `integrations.connect` accepts. */
|
|
153
|
+
export declare const CONNECTOR_FIELDS: readonly ["name", "transport", "auth", "tools"];
|