@stackbone/sdk 0.1.0-alpha.6 → 0.1.0-alpha.7

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 CHANGED
@@ -7,6 +7,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.0-alpha.7] - 2026-06-25
11
+
12
+ ### Added
13
+
14
+ - **Workflow-level Human-in-the-Loop (HITL) on the new `@stackbone/sdk/workflow`
15
+ subpath.** `requestApproval({ token, topic, payload, title, timeout, fallback })`
16
+ pauses a durable workflow on a Workflow SDK hook, records the pause in the
17
+ agent's `approvals` table (joined to the run so it shows in the inbox), and
18
+ races the human decision against the `timeout` — applying the `fallback`
19
+ (`'reject' | 'approve'`) if nobody decides. The caller gates the side-effect on
20
+ `decision.status === 'approved'`. The raw `defineHook` + `sleep` primitives are
21
+ re-exported from the same subpath as the escape hatch. Neither may run inside a
22
+ `"use step"` block (`.create()` is a workflow primitive that suspends the run).
23
+ The subpath statically imports the new OPTIONAL `workflow` peer dependency
24
+ (kept external by the build), so the main `@stackbone/sdk` barrel stays free of
25
+ it — a tool-only agent never eager-loads `workflow`.
26
+ - **`ApprovalRequestOptions.runId`** — `client.approval.request(...)` now accepts
27
+ a `runId` that is written to `approvals.run_id` (FK to `runs.id`), so an
28
+ approval can be tied to the projected run that requested it.
29
+ - **`eveAgent(name)` on the `@stackbone/sdk/workflow` subpath.** Addresses a
30
+ sibling eve agent BY NAME from a workflow `"use step"` and returns eve's `Client`
31
+ as-is (a thin wrapper — drive it with eve's own `.session()` / `.send()` API).
32
+ The name resolves to a URL through the emulator-injected `AGENT_URLS` map; each
33
+ request is signed with the shared `HMAC_SECRET` (timestamp-bound bearer under
34
+ `x-stackbone-timestamp` / `x-stackbone-workflow-signature`, verified inside a
35
+ 5-minute drift window) so the agent's channel auth accepts the co-located hop.
36
+ The helper statically imports the new OPTIONAL `eve` peer dependency (kept
37
+ external by the build), so the main `@stackbone/sdk` barrel stays free of it.
38
+ - **`stackbone.agent(id)` — the namespaced sibling-agent member on the ambient
39
+ client.** `stackbone.agent('support').session().send({ ... })` produces the same
40
+ signed workflow→agent call and the same eve `Client` API as the now-`@deprecated`
41
+ top-level `eveAgent('support')` on the `/workflow` subpath, narrowed by the same
42
+ augmentable `AgentRegistry` (declared names autocomplete, a typo is a compile
43
+ error). The crux is peer isolation: unlike `eveAgent`, the member imports NO `eve`
44
+ peer at module load — selecting the agent and opening a session are synchronous,
45
+ and only the session's already-async leaves (`send` / `stream`) resolve
46
+ `eve/client` via dynamic import on first use, then memoise it so conversation
47
+ state carries across turns. A tool-only agent that imports `@stackbone/sdk` and
48
+ only touches `stackbone.config` never loads `eve`. Verified by a no-peer import
49
+ regression test (the main barrel `index.js` statically imports neither `eve` nor
50
+ `workflow`).
51
+ - **`stackbone.agent(id).session()` is now a DROP-IN twin of eve's native
52
+ `ClientSession`.** A workflow written against the eager `eveAgent(...)` path keeps
53
+ compiling verbatim when it swaps to `stackbone.agent(...)`: `session.send<T>(...)`
54
+ returns eve's `MessageResponse<T>` (so both `.result()` — typed `.data` — and
55
+ `for await...of` work), `session.session(state)` accepts eve's nominal
56
+ `SessionState` (`{ continuationToken?, sessionId?, streamIndex }`), and
57
+ `session.state` exposes the advanced cursor to persist between turns. These
58
+ peer-free structural mirrors are exported from the main barrel as
59
+ `EveMessageResponse` / `EveMessageResult` / `EveSessionState` / `EveStreamEvent` /
60
+ `SendTurnInput` (plus `AgentAccessor` / `LazyAgentClient` / `LazyAgentSession` /
61
+ `AgentRegistry`) so a workflow can name the handle, the response, the cursor and
62
+ the forwarded stream frame without reaching for the optional `eve` peer.
63
+ Previously the lazy session typed `send` as `Promise<unknown>` and omitted
64
+ `state`, so those usages did not type-check — only the eager path did.
65
+
66
+ ### Deprecated
67
+
68
+ - **`eveAgent(name)`** (on the `@stackbone/sdk/workflow` subpath) in favour of the
69
+ namespaced `stackbone.agent(id)` from the main barrel. The old export still works
70
+ and is byte-identical on the wire; it just resolves the `eve` peer eagerly (the
71
+ subpath already imports it) instead of lazily.
72
+
73
+ ### Documentation
74
+
75
+ - **Public docs + starter templates moved to the namespaced reach-by-id surface.**
76
+ The developer wiki gains a dedicated `stackbone.agent` page, documents
77
+ `stackbone.connection(id)` alongside the workspace-broker `client.legacyConnections`,
78
+ and shows the ambient `stackbone` handle with both reach-by-id members in the SDK
79
+ overview — each with a note that the top-level `eveAgent(...)` / bare
80
+ `connection(...)` forms are deprecated aliases. The starter's `AGENTS.md` now
81
+ models `stackbone.agent(...)` / `stackbone.connection(...)` for agent-to-agent and
82
+ connector calls. The CLI's inline `stackbone docs` bundle is regenerated from the
83
+ wiki so the new surface ships with the next CLI release.
84
+
10
85
  ## [0.1.0-alpha.6] - 2026-06-17
11
86
 
12
87
  ### Added
@@ -0,0 +1,118 @@
1
+ /**
2
+ * WHO a connector call is made on behalf of — the scoping principal the broker
3
+ * keys its credential cache/grant against. A STRUCTURAL mirror of the broker
4
+ * contract's `@stackbone/connect-contract` `Principal` union (app / user /
5
+ * client-credentials / jwt-bearer); defined locally rather than imported so the
6
+ * SDK connect surface stays a self-contained leaf (the sibling eve adapter follows
7
+ * the same discipline — it never pulls the platform contract into the published
8
+ * SDK tarball). It is structurally compatible, so a value typed against the
9
+ * contract's `Principal` is assignable here and vice-versa.
10
+ */
11
+ type Principal = {
12
+ readonly type: 'app';
13
+ } | {
14
+ readonly type: 'user';
15
+ readonly id: string;
16
+ readonly issuer: string;
17
+ readonly attributes?: Readonly<Record<string, string | readonly string[]>>;
18
+ } | {
19
+ readonly type: 'client-credentials';
20
+ readonly serviceAccountId: string;
21
+ } | {
22
+ readonly type: 'jwt-bearer';
23
+ readonly subject: string;
24
+ readonly issuer: string;
25
+ };
26
+
27
+ /** Options accepted by `callConnector` / `connection(...).call`. */
28
+ interface CallConnectorOptions {
29
+ /**
30
+ * WHO the connector call is made on behalf of. Defaults to `{ type: 'app' }`
31
+ * (the agent's own service-account credential). A `user` principal scopes the
32
+ * broker token issuer to that end user; `client-credentials` / `jwt-bearer` are
33
+ * the broker's M2M auth modes.
34
+ */
35
+ readonly principal?: Principal;
36
+ }
37
+ /**
38
+ * An `Error` carrying the broker's machine-readable `code` so a workflow matches
39
+ * the failure BY CODE STRING (never `instanceof` — under bundling the SDK and the
40
+ * workflow can hold different class identities; the code is the stable signal).
41
+ */
42
+ interface ConnectorCallError extends Error {
43
+ /** The broker error code (`invalid_args`, `credential_error`, `timeout`, …). */
44
+ readonly code: string;
45
+ }
46
+ /**
47
+ * Call a Stackbone Connect connector operation directly from a workflow.
48
+ *
49
+ * POSTs `{ connector, operation, args, principal, installationId }` to the
50
+ * agent-facing broker (`POST /api/connect/execute`) with HMAC scheme-A headers.
51
+ * On `{ ok: true, output }` it returns the operation `output`; on
52
+ * `{ ok: false, error }` (or any non-2xx) it THROWS a `ConnectorCallError` whose
53
+ * `.code` is the broker error code and `.message` the broker message.
54
+ *
55
+ * ```ts
56
+ * const channels = await callConnector('slack', 'conversations.list');
57
+ * await callConnector('slack', 'chat.postMessage', { channel: 'C123', text: 'hi' });
58
+ * ```
59
+ *
60
+ * @param connector The connect connector id registered in Studio.
61
+ * @param operation The operation / tool id to run on that connector.
62
+ * @param args The operation arguments (path/query/body params). Defaults `{}`.
63
+ * @param opts Optional `{ principal }` — defaults to `{ type: 'app' }`.
64
+ */
65
+ declare function callConnector(connector: string, operation: string, args?: Readonly<Record<string, unknown>>, opts?: CallConnectorOptions): Promise<unknown>;
66
+ /** The dynamic escape hatch present on every `connection(id)` handle. */
67
+ interface ConnectorHandle {
68
+ /**
69
+ * Run ANY operation on this connector by id — the untyped escape hatch, always
70
+ * available (use it for an operation id that is not a JS identifier, e.g. a
71
+ * dotted `chat.postMessage`, or a connector with no generated types). Sugar for
72
+ * `callConnector(id, operation, args, opts)`.
73
+ */
74
+ call(operation: string, args?: Readonly<Record<string, unknown>>, opts?: CallConnectorOptions): Promise<unknown>;
75
+ }
76
+ /**
77
+ * Augmentable registry of TYPED connector operations, keyed by the VERBATIM
78
+ * connector id (`'stub-mail'`, `'slack'`). The Stackbone Connect type generator
79
+ * (`stackbone dev` writes `.stackbone/connect.d.ts`) MERGES one member per connector
80
+ * into this interface via a `declare module` augmentation, each a map of the
81
+ * connector's operations → typed `(args) => Promise<output>`, derived from the
82
+ * introspected JSON Schema. Empty by default — with no generated types,
83
+ * `connection(id)` offers only the dynamic `.call(...)`.
84
+ *
85
+ * Same id-keyed-registry mechanism as `eveAgent`'s `AgentRegistry`: the selection
86
+ * style is unified — address an entity by its verbatim id, get a typed handle.
87
+ */
88
+ interface StackboneConnections {
89
+ }
90
+ /**
91
+ * The typed handle for a connector id: its generated operations (when the id is a
92
+ * known key of `StackboneConnections`) intersected with the dynamic `.call` escape
93
+ * hatch (always present). An unknown id resolves to just `ConnectorHandle`.
94
+ */
95
+ type ConnectorOf<Id extends string> = (Id extends keyof StackboneConnections ? StackboneConnections[Id] : unknown) & ConnectorHandle;
96
+ /**
97
+ * The exported `connection`: select a connector by its verbatim id and get a typed
98
+ * handle. A known id (a key of the generated `StackboneConnections`) yields its
99
+ * typed operations + `.call`; any other string yields the dynamic `ConnectorHandle`.
100
+ */
101
+ interface ConnectionAccessor {
102
+ <Id extends keyof StackboneConnections>(id: Id): ConnectorOf<Id>;
103
+ (id: string): ConnectorHandle;
104
+ }
105
+ /**
106
+ * Call Stackbone Connect connectors. Select by verbatim id, then call an operation —
107
+ * typed when `.stackbone/connect.d.ts` is generated, dynamic via `.call` always:
108
+ *
109
+ * ```ts
110
+ * await connection('stub-mail').sendMail({ to, subject, body }); // typed
111
+ * await connection('slack').call('chat.postMessage', { ... }); // dynamic escape hatch
112
+ * ```
113
+ *
114
+ * Mirrors `eveAgent('agent-name')`: one id-keyed selection style across the SDK.
115
+ */
116
+ declare const connection: ConnectionAccessor;
117
+
118
+ export { type CallConnectorOptions as C, type Principal as P, type StackboneConnections as S, type ConnectionAccessor as a, type ConnectorCallError as b, type ConnectorHandle as c, callConnector as d, connection as e };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * WHO a connector call is made on behalf of — the scoping principal the broker
3
+ * keys its credential cache/grant against. A STRUCTURAL mirror of the broker
4
+ * contract's `@stackbone/connect-contract` `Principal` union (app / user /
5
+ * client-credentials / jwt-bearer); defined locally rather than imported so the
6
+ * SDK connect surface stays a self-contained leaf (the sibling eve adapter follows
7
+ * the same discipline — it never pulls the platform contract into the published
8
+ * SDK tarball). It is structurally compatible, so a value typed against the
9
+ * contract's `Principal` is assignable here and vice-versa.
10
+ */
11
+ type Principal = {
12
+ readonly type: 'app';
13
+ } | {
14
+ readonly type: 'user';
15
+ readonly id: string;
16
+ readonly issuer: string;
17
+ readonly attributes?: Readonly<Record<string, string | readonly string[]>>;
18
+ } | {
19
+ readonly type: 'client-credentials';
20
+ readonly serviceAccountId: string;
21
+ } | {
22
+ readonly type: 'jwt-bearer';
23
+ readonly subject: string;
24
+ readonly issuer: string;
25
+ };
26
+
27
+ /** Options accepted by `callConnector` / `connection(...).call`. */
28
+ interface CallConnectorOptions {
29
+ /**
30
+ * WHO the connector call is made on behalf of. Defaults to `{ type: 'app' }`
31
+ * (the agent's own service-account credential). A `user` principal scopes the
32
+ * broker token issuer to that end user; `client-credentials` / `jwt-bearer` are
33
+ * the broker's M2M auth modes.
34
+ */
35
+ readonly principal?: Principal;
36
+ }
37
+ /**
38
+ * An `Error` carrying the broker's machine-readable `code` so a workflow matches
39
+ * the failure BY CODE STRING (never `instanceof` — under bundling the SDK and the
40
+ * workflow can hold different class identities; the code is the stable signal).
41
+ */
42
+ interface ConnectorCallError extends Error {
43
+ /** The broker error code (`invalid_args`, `credential_error`, `timeout`, …). */
44
+ readonly code: string;
45
+ }
46
+ /**
47
+ * Call a Stackbone Connect connector operation directly from a workflow.
48
+ *
49
+ * POSTs `{ connector, operation, args, principal, installationId }` to the
50
+ * agent-facing broker (`POST /api/connect/execute`) with HMAC scheme-A headers.
51
+ * On `{ ok: true, output }` it returns the operation `output`; on
52
+ * `{ ok: false, error }` (or any non-2xx) it THROWS a `ConnectorCallError` whose
53
+ * `.code` is the broker error code and `.message` the broker message.
54
+ *
55
+ * ```ts
56
+ * const channels = await callConnector('slack', 'conversations.list');
57
+ * await callConnector('slack', 'chat.postMessage', { channel: 'C123', text: 'hi' });
58
+ * ```
59
+ *
60
+ * @param connector The connect connector id registered in Studio.
61
+ * @param operation The operation / tool id to run on that connector.
62
+ * @param args The operation arguments (path/query/body params). Defaults `{}`.
63
+ * @param opts Optional `{ principal }` — defaults to `{ type: 'app' }`.
64
+ */
65
+ declare function callConnector(connector: string, operation: string, args?: Readonly<Record<string, unknown>>, opts?: CallConnectorOptions): Promise<unknown>;
66
+ /** The dynamic escape hatch present on every `connection(id)` handle. */
67
+ interface ConnectorHandle {
68
+ /**
69
+ * Run ANY operation on this connector by id — the untyped escape hatch, always
70
+ * available (use it for an operation id that is not a JS identifier, e.g. a
71
+ * dotted `chat.postMessage`, or a connector with no generated types). Sugar for
72
+ * `callConnector(id, operation, args, opts)`.
73
+ */
74
+ call(operation: string, args?: Readonly<Record<string, unknown>>, opts?: CallConnectorOptions): Promise<unknown>;
75
+ }
76
+ /**
77
+ * Augmentable registry of TYPED connector operations, keyed by the VERBATIM
78
+ * connector id (`'stub-mail'`, `'slack'`). The Stackbone Connect type generator
79
+ * (`stackbone dev` writes `.stackbone/connect.d.ts`) MERGES one member per connector
80
+ * into this interface via a `declare module` augmentation, each a map of the
81
+ * connector's operations → typed `(args) => Promise<output>`, derived from the
82
+ * introspected JSON Schema. Empty by default — with no generated types,
83
+ * `connection(id)` offers only the dynamic `.call(...)`.
84
+ *
85
+ * Same id-keyed-registry mechanism as `eveAgent`'s `AgentRegistry`: the selection
86
+ * style is unified — address an entity by its verbatim id, get a typed handle.
87
+ */
88
+ interface StackboneConnections {
89
+ }
90
+ /**
91
+ * The typed handle for a connector id: its generated operations (when the id is a
92
+ * known key of `StackboneConnections`) intersected with the dynamic `.call` escape
93
+ * hatch (always present). An unknown id resolves to just `ConnectorHandle`.
94
+ */
95
+ type ConnectorOf<Id extends string> = (Id extends keyof StackboneConnections ? StackboneConnections[Id] : unknown) & ConnectorHandle;
96
+ /**
97
+ * The exported `connection`: select a connector by its verbatim id and get a typed
98
+ * handle. A known id (a key of the generated `StackboneConnections`) yields its
99
+ * typed operations + `.call`; any other string yields the dynamic `ConnectorHandle`.
100
+ */
101
+ interface ConnectionAccessor {
102
+ <Id extends keyof StackboneConnections>(id: Id): ConnectorOf<Id>;
103
+ (id: string): ConnectorHandle;
104
+ }
105
+ /**
106
+ * Call Stackbone Connect connectors. Select by verbatim id, then call an operation —
107
+ * typed when `.stackbone/connect.d.ts` is generated, dynamic via `.call` always:
108
+ *
109
+ * ```ts
110
+ * await connection('stub-mail').sendMail({ to, subject, body }); // typed
111
+ * await connection('slack').call('chat.postMessage', { ... }); // dynamic escape hatch
112
+ * ```
113
+ *
114
+ * Mirrors `eveAgent('agent-name')`: one id-keyed selection style across the SDK.
115
+ */
116
+ declare const connection: ConnectionAccessor;
117
+
118
+ export { type CallConnectorOptions as C, type Principal as P, type StackboneConnections as S, type ConnectionAccessor as a, type ConnectorCallError as b, type ConnectorHandle as c, callConnector as d, connection as e };
package/connect.cjs ADDED
@@ -0,0 +1,270 @@
1
+ 'use strict';
2
+
3
+ var connections = require('eve/connections');
4
+ var crypto = require('crypto');
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var TIMESTAMP_HEADER = "x-stackbone-timestamp";
9
+ var SIGNATURE_HEADER = "x-stackbone-workflow-signature";
10
+ function signBrokerHeaders() {
11
+ const secret = process.env["HMAC_SECRET"] ?? "";
12
+ const timestamp = String(Date.now());
13
+ const signature = crypto.createHmac("sha256", secret).update(timestamp).digest("hex");
14
+ return {
15
+ [TIMESTAMP_HEADER]: timestamp,
16
+ [SIGNATURE_HEADER]: signature
17
+ };
18
+ }
19
+ __name(signBrokerHeaders, "signBrokerHeaders");
20
+ function brokerBaseUrl() {
21
+ const url = process.env["STACKBONE_API_URL"];
22
+ if (!url) {
23
+ throw new Error("STACKBONE_API_URL is not set. The Stackbone runtime injects the broker base URL; run this agent through `stackbone dev`.");
24
+ }
25
+ return url.replace(/\/+$/, "");
26
+ }
27
+ __name(brokerBaseUrl, "brokerBaseUrl");
28
+ function installationId() {
29
+ const id = process.env["STACKBONE_INSTALLATION_ID"];
30
+ if (!id) {
31
+ throw new Error("STACKBONE_INSTALLATION_ID is not set. The Stackbone runtime injects it; run this agent through `stackbone dev`.");
32
+ }
33
+ return id;
34
+ }
35
+ __name(installationId, "installationId");
36
+
37
+ // src/surfaces/external/connect/connect.ts
38
+ var TOKEN_PATH = "/api/connect/token";
39
+ function brokerCodeToEveError(connectorId, code, message) {
40
+ switch (code) {
41
+ case "no_token":
42
+ case "user_authorization_required":
43
+ return new connections.ConnectionAuthorizationRequiredError(connectorId, message ? {
44
+ message
45
+ } : void 0);
46
+ case "connector_installation_required":
47
+ case "app_not_installed":
48
+ return new connections.ConnectionAuthorizationFailedError(connectorId, {
49
+ ...message ? {
50
+ message
51
+ } : {},
52
+ reason: "app_not_installed",
53
+ retryable: false
54
+ });
55
+ case "principal_required":
56
+ return new connections.ConnectionAuthorizationFailedError(connectorId, {
57
+ ...message ? {
58
+ message
59
+ } : {},
60
+ reason: "principal_required",
61
+ retryable: false
62
+ });
63
+ default:
64
+ return new connections.ConnectionAuthorizationFailedError(connectorId, {
65
+ message: message ?? `Stackbone Connect broker rejected the token request (code "${code}").`,
66
+ reason: code,
67
+ retryable: false
68
+ });
69
+ }
70
+ }
71
+ __name(brokerCodeToEveError, "brokerCodeToEveError");
72
+ async function readBrokerError(response) {
73
+ const body = await response.json().catch(() => null);
74
+ if (body && typeof body.code === "string") {
75
+ return {
76
+ code: body.code,
77
+ ...body.message !== void 0 ? {
78
+ message: body.message
79
+ } : {}
80
+ };
81
+ }
82
+ return {
83
+ code: "credential_error",
84
+ message: `Broker responded ${response.status}.`
85
+ };
86
+ }
87
+ __name(readBrokerError, "readBrokerError");
88
+ async function fetchBrokerToken(connectorId) {
89
+ const body = JSON.stringify({
90
+ connector: connectorId,
91
+ principal: {
92
+ type: "app"
93
+ },
94
+ installationId: installationId()
95
+ });
96
+ let response;
97
+ try {
98
+ response = await fetch(`${brokerBaseUrl()}${TOKEN_PATH}`, {
99
+ method: "POST",
100
+ headers: {
101
+ "content-type": "application/json",
102
+ ...signBrokerHeaders()
103
+ },
104
+ body
105
+ });
106
+ } catch (cause) {
107
+ throw new connections.ConnectionAuthorizationFailedError(connectorId, {
108
+ message: `Stackbone Connect broker is unreachable: ${cause instanceof Error ? cause.message : String(cause)}`,
109
+ reason: "execute_failed",
110
+ retryable: true
111
+ });
112
+ }
113
+ if (!response.ok) {
114
+ const error = await readBrokerError(response);
115
+ throw brokerCodeToEveError(connectorId, error.code, error.message);
116
+ }
117
+ const minted = await response.json().catch(() => null);
118
+ if (!minted || typeof minted.token !== "string") {
119
+ throw new connections.ConnectionAuthorizationFailedError(connectorId, {
120
+ message: "Stackbone Connect broker returned a malformed token response.",
121
+ reason: "invalid_output",
122
+ retryable: false
123
+ });
124
+ }
125
+ return minted.expiresAt !== void 0 ? {
126
+ token: minted.token,
127
+ expiresAt: minted.expiresAt
128
+ } : {
129
+ token: minted.token
130
+ };
131
+ }
132
+ __name(fetchBrokerToken, "fetchBrokerToken");
133
+ async function evictBrokerToken(connectorId) {
134
+ try {
135
+ await fetch(`${brokerBaseUrl()}${TOKEN_PATH.replace(/\/token$/, "/evict")}`, {
136
+ method: "POST",
137
+ headers: {
138
+ "content-type": "application/json",
139
+ ...signBrokerHeaders()
140
+ },
141
+ body: JSON.stringify({
142
+ connector: connectorId,
143
+ principal: {
144
+ type: "app"
145
+ },
146
+ installationId: process.env["STACKBONE_INSTALLATION_ID"]
147
+ })
148
+ });
149
+ } catch {
150
+ }
151
+ }
152
+ __name(evictBrokerToken, "evictBrokerToken");
153
+ function connect(connectorId) {
154
+ return {
155
+ principalType: "app",
156
+ stackboneConnect: {
157
+ connector: connectorId
158
+ },
159
+ getToken(_opts) {
160
+ return fetchBrokerToken(connectorId);
161
+ },
162
+ evict(_opts) {
163
+ return evictBrokerToken(connectorId);
164
+ }
165
+ };
166
+ }
167
+ __name(connect, "connect");
168
+
169
+ // src/surfaces/external/connect/channel-helpers.ts
170
+ function withConnect(definition, connectorId) {
171
+ return {
172
+ ...definition,
173
+ auth: connect(connectorId)
174
+ };
175
+ }
176
+ __name(withConnect, "withConnect");
177
+ function connectHeaders(connectorId, headerName = "X-Api-Key") {
178
+ const auth = connect(connectorId);
179
+ return async () => {
180
+ const { token } = await auth.getToken({
181
+ principal: {
182
+ type: "app"
183
+ },
184
+ connection: {
185
+ url: ""
186
+ }
187
+ });
188
+ return {
189
+ [headerName]: token
190
+ };
191
+ };
192
+ }
193
+ __name(connectHeaders, "connectHeaders");
194
+
195
+ // src/surfaces/external/connect/call-connector.ts
196
+ var EXECUTE_PATH = "/api/connect/execute";
197
+ function connectorCallError(code, message) {
198
+ const error = new Error(message);
199
+ error.code = code;
200
+ return error;
201
+ }
202
+ __name(connectorCallError, "connectorCallError");
203
+ function errorFromBody(status, body) {
204
+ if (body && body.ok === false && body.error && typeof body.error.code === "string") {
205
+ return connectorCallError(body.error.code, body.error.message ?? `Stackbone Connect broker rejected the call (code "${body.error.code}").`);
206
+ }
207
+ return connectorCallError("credential_error", `Stackbone Connect broker responded ${status}.`);
208
+ }
209
+ __name(errorFromBody, "errorFromBody");
210
+ async function callConnector(connector, operation, args, opts) {
211
+ const principal = opts?.principal ?? {
212
+ type: "app"
213
+ };
214
+ const url = `${brokerBaseUrl()}${EXECUTE_PATH}`;
215
+ const body = JSON.stringify({
216
+ connector,
217
+ operation,
218
+ args: args ?? {},
219
+ principal,
220
+ installationId: installationId()
221
+ });
222
+ let response;
223
+ try {
224
+ response = await fetch(url, {
225
+ method: "POST",
226
+ headers: {
227
+ "content-type": "application/json",
228
+ ...signBrokerHeaders()
229
+ },
230
+ body
231
+ });
232
+ } catch (cause) {
233
+ throw connectorCallError("execute_failed", `Stackbone Connect broker is unreachable: ${cause instanceof Error ? cause.message : String(cause)}`);
234
+ }
235
+ const parsed = await response.json().catch(() => null);
236
+ if (!response.ok || parsed && parsed.ok === false) {
237
+ throw errorFromBody(response.status, parsed ?? null);
238
+ }
239
+ if (!parsed || parsed.ok !== true) {
240
+ throw connectorCallError("invalid_output", "Stackbone Connect broker returned a malformed execute response.");
241
+ }
242
+ return parsed.output;
243
+ }
244
+ __name(callConnector, "callConnector");
245
+ var connectorHandle = /* @__PURE__ */ __name((id) => new Proxy(/* @__PURE__ */ Object.create(null), {
246
+ get(_target, prop) {
247
+ if (typeof prop !== "string" || prop === "then") return void 0;
248
+ if (prop === "call") {
249
+ return (operation, args, opts) => callConnector(id, operation, args, opts);
250
+ }
251
+ return (args, opts) => callConnector(id, prop, args, opts);
252
+ }
253
+ }), "connectorHandle");
254
+ var connection = /* @__PURE__ */ __name((id) => connectorHandle(id), "connection");
255
+
256
+ Object.defineProperty(exports, "ConnectionAuthorizationFailedError", {
257
+ enumerable: true,
258
+ get: function () { return connections.ConnectionAuthorizationFailedError; }
259
+ });
260
+ Object.defineProperty(exports, "ConnectionAuthorizationRequiredError", {
261
+ enumerable: true,
262
+ get: function () { return connections.ConnectionAuthorizationRequiredError; }
263
+ });
264
+ exports.callConnector = callConnector;
265
+ exports.connect = connect;
266
+ exports.connectHeaders = connectHeaders;
267
+ exports.connection = connection;
268
+ exports.withConnect = withConnect;
269
+ //# sourceMappingURL=connect.cjs.map
270
+ //# sourceMappingURL=connect.cjs.map