@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 +75 -0
- package/call-connector-CYDw_yG5.d.cts +118 -0
- package/call-connector-CYDw_yG5.d.ts +118 -0
- package/connect.cjs +270 -0
- package/connect.cjs.map +1 -0
- package/connect.d.cts +94 -0
- package/connect.d.ts +94 -0
- package/connect.js +257 -0
- package/connect.js.map +1 -0
- package/index.cjs +380 -60
- package/index.cjs.map +1 -1
- package/index.d.cts +341 -81
- package/index.d.ts +341 -81
- package/index.js +375 -62
- package/index.js.map +1 -1
- package/observability/index.cjs +1 -268
- package/observability/index.cjs.map +1 -1
- package/observability/index.d.cts +1 -96
- package/observability/index.d.ts +1 -96
- package/observability/index.js +2 -264
- package/observability/index.js.map +1 -1
- package/package.json +33 -1
- package/stackbone-sdk-0.1.0-alpha.7.tgz +0 -0
- package/workflow-scheduler-DXCNKDOS.d.cts +119 -0
- package/workflow-scheduler-DXCNKDOS.d.ts +119 -0
- package/workflow.cjs +17417 -0
- package/workflow.cjs.map +1 -0
- package/workflow.d.cts +207 -0
- package/workflow.d.ts +207 -0
- package/workflow.js +17392 -0
- package/workflow.js.map +1 -0
- package/stackbone-sdk-0.1.0-alpha.6.tgz +0 -0
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
|