@apteva/web-sdk 0.5.5 → 0.7.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 +18 -0
- package/README.md +232 -2
- package/dist/apps.d.ts +34 -0
- package/dist/apps.d.ts.map +1 -0
- package/dist/apps.js +30 -0
- package/dist/apps.js.map +1 -0
- package/dist/client.d.ts +31 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +316 -107
- package/dist/client.js.map +1 -1
- package/dist/components.d.ts +43 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +37 -0
- package/dist/components.js.map +1 -0
- package/dist/extensions.d.ts +17 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +4 -0
- package/dist/extensions.js.map +1 -0
- package/dist/frontend.d.ts +40 -0
- package/dist/frontend.d.ts.map +1 -0
- package/dist/frontend.js +120 -0
- package/dist/frontend.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +23 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 — 2026-09-07
|
|
4
|
+
|
|
5
|
+
- Load app-owned frontend bundles from installed apps with `client.apps.load`; no per-app npm package is required.
|
|
6
|
+
- Share authenticated scoped requests, verify asset hashes, check versions/React compatibility, and create fresh app clients and UI contexts.
|
|
7
|
+
- Reference-count scoped styles and expose cancellation and disposal.
|
|
8
|
+
- Preserve local extension/registry APIs and existing chat transports.
|
|
9
|
+
|
|
10
|
+
## 0.6.0 — 2026-09-07
|
|
11
|
+
|
|
12
|
+
- Add typed app-owned extensions through `defineAppExtension` and `client.use`.
|
|
13
|
+
- Add project/install-scoped app handles, HTTP/MCP request options, and app SSE subscriptions sharing live client credentials.
|
|
14
|
+
- Add framework-independent component references, manifest metadata types and a local export registry.
|
|
15
|
+
- Expose installed-app discovery and explicit metadata/version compatibility checks.
|
|
16
|
+
- Fix caller cancellation being overridden by request timeouts; retain cancellation and timeouts through response-body reads.
|
|
17
|
+
- Add abortable fetch subscriptions with SSE resume IDs, optional cursor queries, bounded opt-in deduplication, configurable reconnect delay and terminal auth handling.
|
|
18
|
+
- Add coordinated host-owned access-token renewal without automatically replaying HTTP requests.
|
|
19
|
+
- Preserve legacy Channel Chat APIs; app-specific extensions and UI stay in app-owned packages.
|
|
20
|
+
|
|
3
21
|
## 0.5.5 — 2026-08-15
|
|
4
22
|
|
|
5
23
|
- Add a generic, opaque `accessToken` credential to `AptevaClient`, including
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @apteva/web-sdk
|
|
2
2
|
|
|
3
|
-
Browser SDK for calling [apteva-server](https://github.com/apteva/server). Framework-agnostic.
|
|
3
|
+
Browser SDK for calling [apteva-server](https://github.com/apteva/server). Framework-agnostic.
|
|
4
4
|
|
|
5
5
|
- **Auth transport** — session cookies, private API keys, and opaque application-user access tokens. The SDK does not depend on a particular token issuer.
|
|
6
6
|
- **App data** — `client.app("crm").get("/contacts")` reverse-proxies through `/api/apps/<name>/*`.
|
|
@@ -39,6 +39,58 @@ const { leads } = await apteva.app("flexylead").tool<{ leads: Lead[] }>(
|
|
|
39
39
|
);
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Load an installed app without its npm package
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import * as React from "react";
|
|
46
|
+
import { AptevaClient } from "@apteva/web-sdk";
|
|
47
|
+
|
|
48
|
+
const client = new AptevaClient({ baseURL, accessToken });
|
|
49
|
+
const loaded = await client.apps.load("conversations", {
|
|
50
|
+
projectId, installId, react: React,
|
|
51
|
+
});
|
|
52
|
+
const Chat = loaded.components["conversation-chat"];
|
|
53
|
+
// In JavaScript/JSX; TypeScript hosts can supply their component/client types
|
|
54
|
+
// through apps.load<ClientContract, React.ComponentType<HostProps>>(...).
|
|
55
|
+
<Chat conversations={loaded.client} agentId={agentId} />
|
|
56
|
+
// After unmounting all consumers:
|
|
57
|
+
loaded.dispose();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Only the shared SDK and the host's React are installed. The selected app serves
|
|
61
|
+
`/ui/frontend.json`, a headless client module, a UI factory and scoped styles as
|
|
62
|
+
part of its regular app release. Omit `react` for headless loading. Every load
|
|
63
|
+
creates a new client; retain it across renders and replace it when identity or
|
|
64
|
+
scope changes. `clientOptions` passes app-specific options to the client factory.
|
|
65
|
+
|
|
66
|
+
The loader requires explicit project/install selection. Asset fetches use the
|
|
67
|
+
SDK's current authentication, with no tokens in URLs, reject redirects and verify
|
|
68
|
+
SHA-256 hashes before execution. Metadata is fetched fresh; client/UI/styles download in parallel and immutable
|
|
69
|
+
asset URLs reuse the browser cache on later visits. Code caches contain
|
|
70
|
+
no credentials or app client instances. `expectedVersion` can reject unexpected
|
|
71
|
+
app upgrades. UI factories receive the host's React (major version checked), so
|
|
72
|
+
no import map or separate React copy is needed. Styles are reference counted and
|
|
73
|
+
removed by `dispose()`. Pass `signal` to cancel a pending load.
|
|
74
|
+
|
|
75
|
+
Explicit loading executes trusted installed-app code in the host page. HTTPS or
|
|
76
|
+
localhost is required for integrity checks, and CSP must permit `script-src blob:`
|
|
77
|
+
and the loader's inline styles. It does not sandbox app code or grant API access.
|
|
78
|
+
No component URLs from user messages are accepted as loader entry points.
|
|
79
|
+
|
|
80
|
+
### App frontend contract
|
|
81
|
+
|
|
82
|
+
Ship `/ui/frontend.json` with schema `apteva-app-frontend/v1`, `app`, `version`,
|
|
83
|
+
and `client: { path, sha256 }`. The optional `ui` asset also declares `reactMajor`
|
|
84
|
+
and a `components` name array; `styles` is an optional CSS asset. Paths stay under
|
|
85
|
+
`/ui/`, contain no queries or traversal, and use lowercase SHA-256 hex hashes.
|
|
86
|
+
Content-addressed filenames keep an update from mixing incompatible assets.
|
|
87
|
+
|
|
88
|
+
The client module exports `createClient({ app }, options)` and the UI module
|
|
89
|
+
exports `createFrontend({ react })`, returning `{ components }`. Each module must
|
|
90
|
+
be self-contained, without external imports, relative chunks or runtime require.
|
|
91
|
+
The UI module should instantiate its contexts inside the factory. CSS should be
|
|
92
|
+
scoped to the app. See Conversations' `frontend/build-app.ts` for the implementation.
|
|
93
|
+
|
|
42
94
|
## Authentication carriers
|
|
43
95
|
|
|
44
96
|
With no bearer credential, the SDK sends `credentials: "include"` so an
|
|
@@ -93,6 +145,184 @@ If the MCP call returns a JSON-RPC error, the SDK throws `AptevaError(-1, messag
|
|
|
93
145
|
|
|
94
146
|
For lower-level usage, `client.callTool(appName, toolName, args)` and the standalone `unwrapMCP(envelope)` are both exported.
|
|
95
147
|
|
|
148
|
+
## App-owned extensions and web components
|
|
149
|
+
|
|
150
|
+
Apps can serve their client and components with the normal app release through
|
|
151
|
+
`apps.load`, or supply local modules to `client.use` and the component registry.
|
|
152
|
+
The core SDK has no React dependency; a host that loads React UI supplies its own
|
|
153
|
+
React instance. Dashboard wrappers and external hosts share component source and
|
|
154
|
+
an authenticated app handle. A per-app npm package is optional.
|
|
155
|
+
|
|
156
|
+
### Typed extensions
|
|
157
|
+
|
|
158
|
+
`defineAppExtension` preserves the extension's return type. `client.use` supplies
|
|
159
|
+
an authenticated app handle. Each call creates a new instance: retain it in your
|
|
160
|
+
host (for example with React's `useMemo`), and close subscriptions when unmounting.
|
|
161
|
+
An async `create` is supported; `use` then returns its Promise unchanged.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { AptevaClient, defineAppExtension } from "@apteva/web-sdk";
|
|
165
|
+
|
|
166
|
+
// Illustrative app-owned API. Replace routes and wire types with your app's contract.
|
|
167
|
+
const itemsExtension = defineAppExtension({
|
|
168
|
+
app: "example",
|
|
169
|
+
create: ({ app }) => ({
|
|
170
|
+
list: (signal?: AbortSignal) =>
|
|
171
|
+
app.get<Array<{ id: string; title: string }>>("/items", { signal }),
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const client = new AptevaClient({ baseURL: "https://agents.example.com", accessToken });
|
|
176
|
+
const items = client.use(itemsExtension, { projectId: "p1", installId: 42 });
|
|
177
|
+
const rows = await items.list(); // typed result
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Extensions contain the app's own business logic and API contract validation.
|
|
181
|
+
A Conversations extension and its chat, inbox, and approval UI belong in the
|
|
182
|
+
Conversations package. The existing `client.chat` remains a Channel Chat API;
|
|
183
|
+
it is not redirected to Conversations.
|
|
184
|
+
|
|
185
|
+
### Shared app transport
|
|
186
|
+
|
|
187
|
+
`client.app(name, { projectId, installId })` scopes HTTP, MCP, and subscriptions
|
|
188
|
+
together. It exposes `name`, `projectId`, and `installId` for host context and
|
|
189
|
+
reads the client's current credentials on each request or reconnection.
|
|
190
|
+
Explicit handle scope overrides routing query parameters. With no explicit
|
|
191
|
+
project scope, the existing client default and query-override behavior remain.
|
|
192
|
+
The platform still validates the selected installation and project.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const app = client.app("example", { projectId: "p1", installId: 42 });
|
|
196
|
+
const controller = new AbortController();
|
|
197
|
+
|
|
198
|
+
await app.post("/items", { title: "Hello" }, { signal: controller.signal });
|
|
199
|
+
await app.tool("items_list", {}, { signal: controller.signal });
|
|
200
|
+
|
|
201
|
+
const subscription = app.subscribe<{ id: string; title: string }>(
|
|
202
|
+
"/events",
|
|
203
|
+
(row, metadata) => console.log(row, metadata.event, metadata.id),
|
|
204
|
+
{ signal: controller.signal, eventTypes: ["item.updated"] },
|
|
205
|
+
);
|
|
206
|
+
subscription.close();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
All HTTP helpers accept `RequestInit`; MCP accepts it as the third argument.
|
|
210
|
+
Cancellation works alongside the client timeout, including while reading the
|
|
211
|
+
response body. App paths must start with `/` and remain inside the app route.
|
|
212
|
+
Use the raw client methods for platform endpoints. No HTTP method is retried
|
|
213
|
+
automatically. Pass an app-supported idempotency header or body field explicitly
|
|
214
|
+
when the app contract permits a write to be retried.
|
|
215
|
+
|
|
216
|
+
### Component exports and manifest metadata
|
|
217
|
+
|
|
218
|
+
`AppComponentReference` is the existing `{ app, name, props? }` message shape;
|
|
219
|
+
`ChatComponent` remains a compatible name. `AppComponentSpec` mirrors existing
|
|
220
|
+
`provides.ui_components` metadata, including slots, prop schemas and widget
|
|
221
|
+
settings. There is no second server declaration format.
|
|
222
|
+
|
|
223
|
+
`AppComponentRegistry<T>` maps those app/component names to local frontend
|
|
224
|
+
exports. It accepts framework-specific components or host-owned loaders without
|
|
225
|
+
adding a framework dependency. Registration rejects duplicate names atomically
|
|
226
|
+
and returns an idempotent unregister function; unknown references resolve to
|
|
227
|
+
`undefined` so the host can render a fallback.
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { AppComponentRegistry } from "@apteva/web-sdk";
|
|
231
|
+
import { ItemCard } from "./components/ItemCard"; // or an app-owned frontend package
|
|
232
|
+
|
|
233
|
+
const components = new AppComponentRegistry<typeof ItemCard>();
|
|
234
|
+
const unregister = components.register("example", { "item-card": ItemCard });
|
|
235
|
+
const Component = components.resolve({ app: "example", name: "item-card" });
|
|
236
|
+
// Render Component using your framework and validated, appropriate props.
|
|
237
|
+
unregister();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`AppComponentContext` defines a host-injected `app: AppHandle`. Pass this trusted
|
|
241
|
+
context separately from message data; do not let message props replace it.
|
|
242
|
+
The host handles rendering, slots, schema validation, loading/error boundaries,
|
|
243
|
+
theme and component lifecycle. Registry membership alone does not establish
|
|
244
|
+
installation, compatibility, identity or authorization. Existing dashboard
|
|
245
|
+
module loading remains a host concern; installing this SDK does not port an
|
|
246
|
+
existing component's fetch calls or styling automatically.
|
|
247
|
+
|
|
248
|
+
### Installed-app discovery and compatibility
|
|
249
|
+
|
|
250
|
+
`client.apps.list({ projectId? }, requestInit?)` wraps the existing `/api/apps`
|
|
251
|
+
endpoint and defaults to the client's project. This endpoint retains its current
|
|
252
|
+
platform permissions; application-user tokens are not guaranteed access. Hosts
|
|
253
|
+
can instead supply metadata they have already obtained through an authorized
|
|
254
|
+
flow. Extension setup does not automatically perform discovery.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { checkAppCompatibility, assertAppCompatibility } from "@apteva/web-sdk";
|
|
258
|
+
|
|
259
|
+
const installed = (await client.apps.list()).find(app => app.install_id === 42);
|
|
260
|
+
const requirements = {
|
|
261
|
+
app: "example",
|
|
262
|
+
version: { description: "1.x", accepts: (version: string) => version.startsWith("1.") },
|
|
263
|
+
tools: ["items_list"],
|
|
264
|
+
components: ["item-card"],
|
|
265
|
+
};
|
|
266
|
+
const result = checkAppCompatibility(installed, requirements); // { compatible, issues }
|
|
267
|
+
assertAppCompatibility(installed, requirements); // AptevaError if incompatible
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Version policy belongs to the app package: supply a predicate (including a semver
|
|
271
|
+
library if needed). These checks validate advertised app identity, version,
|
|
272
|
+
tools and components. They do not infer API schema versions, backend health,
|
|
273
|
+
permissions or unadvertised capabilities. App extensions should separately
|
|
274
|
+
validate any app-owned versioned API contract they require.
|
|
275
|
+
|
|
276
|
+
### Subscription recovery
|
|
277
|
+
|
|
278
|
+
Generic subscriptions accept `signal`; already-aborted signals open no connection.
|
|
279
|
+
Bearer credentials use authenticated fetch streaming. Cookie clients can opt into
|
|
280
|
+
fetch with `transport: "fetch"`. `lastEventId`, `cursorParam`, `deduplicate`, and
|
|
281
|
+
`reconnectDelayMs` also select fetch streaming, even if an EventSource was supplied.
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
const subscription = app.subscribe("/events", onEvent, {
|
|
285
|
+
signal: controller.signal,
|
|
286
|
+
lastEventId: savedEventId,
|
|
287
|
+
cursorParam: "since_id", // only if this app accepts SSE IDs here
|
|
288
|
+
deduplicate: true, // only if the app promises unique IDs per event
|
|
289
|
+
deduplicationWindow: 1000,
|
|
290
|
+
reconnectDelayMs: 1000,
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Fetch streams preserve SSE IDs across reconnects, send `Last-Event-ID`, and
|
|
295
|
+
optionally send the same ID in the app's cursor query parameter. Deduplication
|
|
296
|
+
is opt-in and bounded to the most recent 1000 delivered IDs by default; ID-less
|
|
297
|
+
updates are always delivered. It does not persist across new subscriptions.
|
|
298
|
+
Message-row IDs and SSE IDs are not interchangeable unless the backend says so.
|
|
299
|
+
Named events still require `eventTypes`. Malformed JSON is skipped; a throwing
|
|
300
|
+
fetch-stream event/open callback is reported and closes the subscription.
|
|
301
|
+
|
|
302
|
+
Reconnect delay defaults to 1000ms. Closing or aborting cancels the active reader
|
|
303
|
+
and any pending reconnect. Fetch streams stop on 204 or 403, and on 401 unless
|
|
304
|
+
credentials can be renewed once before a successful connection. Other failures
|
|
305
|
+
reconnect. Native EventSource retains the browser's own reconnect behavior.
|
|
306
|
+
The SDK cannot supply replay, a gap-free history/live handoff, or durable delivery
|
|
307
|
+
unless the app backend implements those contracts.
|
|
308
|
+
|
|
309
|
+
### Host-owned credential renewal
|
|
310
|
+
|
|
311
|
+
Configure `refreshAccessToken: async () => tokenOrUndefined` on the client to
|
|
312
|
+
reuse your own authentication flow. Concurrent 401s coordinate one in-flight
|
|
313
|
+
refresh. `client.refreshAccessToken()` can also be called proactively; it returns
|
|
314
|
+
whether a new token was applied. A login/logout/token replacement during a
|
|
315
|
+
refresh wins over the late result. An undefined result leaves credentials intact.
|
|
316
|
+
|
|
317
|
+
A failed HTTP request still throws its original 401 and triggers `onUnauthorized`
|
|
318
|
+
after the renewal attempt; it is never replayed. Subsequent calls use the new
|
|
319
|
+
token. Requests with a caller-supplied Authorization header do not trigger
|
|
320
|
+
renewal. Fetch SSE can reconnect once with renewed credentials; another 401
|
|
321
|
+
before a successful connection terminates the subscription. Automatic renewal
|
|
322
|
+
failure preserves the original 401; direct `refreshAccessToken()` calls expose
|
|
323
|
+
hook errors. The SDK transports opaque credentials and makes no identity or
|
|
324
|
+
permission decisions.
|
|
325
|
+
|
|
96
326
|
## Agents
|
|
97
327
|
|
|
98
328
|
`client.agents` wraps the core `/api/agents/*` routes — the running apteva-core child processes.
|
|
@@ -254,7 +484,7 @@ and `conversationKey` atomically resumes the same conversation.
|
|
|
254
484
|
`client.delegatedUsers.create(...)` remains available for older trusted-backend
|
|
255
485
|
integrations, but is deprecated and is no longer the recommended website flow.
|
|
256
486
|
|
|
257
|
-
**Rendering note:** `ChatMessage.components[]` carries the agent's `respond(components=…)` attachments.
|
|
487
|
+
**Rendering note:** `ChatMessage.components[]` carries the agent's `respond(components=…)` attachments. Use `AppComponentRegistry` to resolve these references against app-owned frontend exports, then render them in your host framework. Use `apps.load` to explicitly load a trusted installed app’s frontend before registering its components.
|
|
258
488
|
|
|
259
489
|
A complete reference chat UI (message list, composer, streaming bubbles, optimistic send) lives in [`examples/dashboard/src/components/ChatCard.tsx`](./examples/dashboard/src/components/ChatCard.tsx).
|
|
260
490
|
|
package/dist/apps.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AppComponentSpec } from "./components.js";
|
|
2
|
+
/** Relevant fields of the existing platform /api/apps response. */
|
|
3
|
+
export interface InstalledApp {
|
|
4
|
+
install_id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
project_id: string;
|
|
8
|
+
status: string;
|
|
9
|
+
display_name?: string;
|
|
10
|
+
source?: string;
|
|
11
|
+
ui_components?: AppComponentSpec[];
|
|
12
|
+
surfaces?: {
|
|
13
|
+
mcp_tool_names?: string[];
|
|
14
|
+
http_routes?: string[];
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface AppRequirements {
|
|
18
|
+
app: string;
|
|
19
|
+
/** App packages choose their version policy; no implied wire/API version. */
|
|
20
|
+
version?: {
|
|
21
|
+
description: string;
|
|
22
|
+
accepts(version: string): boolean;
|
|
23
|
+
};
|
|
24
|
+
tools?: readonly string[];
|
|
25
|
+
components?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export interface AppCompatibility {
|
|
28
|
+
compatible: boolean;
|
|
29
|
+
issues: string[];
|
|
30
|
+
}
|
|
31
|
+
/** Checks advertised metadata only. This never grants access to an app. */
|
|
32
|
+
export declare function checkAppCompatibility(installed: InstalledApp | undefined, requirements: AppRequirements): AppCompatibility;
|
|
33
|
+
export declare function assertAppCompatibility(installed: InstalledApp | undefined, requirements: AppRequirements): void;
|
|
34
|
+
//# sourceMappingURL=apps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../src/apps.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE;QACT,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACrE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,YAAY,GAAG,SAAS,EACnC,YAAY,EAAE,eAAe,GAC5B,gBAAgB,CAiBlB;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,YAAY,GAAG,SAAS,EACnC,YAAY,EAAE,eAAe,GAC5B,IAAI,CAGN"}
|
package/dist/apps.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AptevaError } from "./errors.js";
|
|
2
|
+
/** Checks advertised metadata only. This never grants access to an app. */
|
|
3
|
+
export function checkAppCompatibility(installed, requirements) {
|
|
4
|
+
const issues = [];
|
|
5
|
+
if (!installed) {
|
|
6
|
+
issues.push(`app ${requirements.app} is not installed`);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
if (installed.name !== requirements.app)
|
|
10
|
+
issues.push(`expected app ${requirements.app}, got ${installed.name}`);
|
|
11
|
+
if (requirements.version && !requirements.version.accepts(installed.version)) {
|
|
12
|
+
issues.push(`version ${installed.version} does not satisfy ${requirements.version.description}`);
|
|
13
|
+
}
|
|
14
|
+
for (const tool of requirements.tools ?? []) {
|
|
15
|
+
if (!installed.surfaces?.mcp_tool_names?.includes(tool))
|
|
16
|
+
issues.push(`missing tool: ${tool}`);
|
|
17
|
+
}
|
|
18
|
+
for (const name of requirements.components ?? []) {
|
|
19
|
+
if (!installed.ui_components?.some((component) => component.name === name))
|
|
20
|
+
issues.push(`missing component: ${name}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { compatible: issues.length === 0, issues };
|
|
24
|
+
}
|
|
25
|
+
export function assertAppCompatibility(installed, requirements) {
|
|
26
|
+
const result = checkAppCompatibility(installed, requirements);
|
|
27
|
+
if (!result.compatible)
|
|
28
|
+
throw new AptevaError(0, `incompatible app: ${result.issues.join("; ")}`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=apps.js.map
|
package/dist/apps.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps.js","sourceRoot":"","sources":["../src/apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgC1C,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB,CACnC,SAAmC,EACnC,YAA6B;IAE7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,OAAO,YAAY,CAAC,GAAG,mBAAmB,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACN,IAAI,SAAS,CAAC,IAAI,KAAK,YAAY,CAAC,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,YAAY,CAAC,GAAG,SAAS,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;QAChH,IAAI,YAAY,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7E,MAAM,CAAC,IAAI,CAAC,WAAW,SAAS,CAAC,OAAO,qBAAqB,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACnG,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YACjD,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC;QACxH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,SAAmC,EACnC,YAA6B;IAE7B,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9D,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,MAAM,IAAI,WAAW,CAAC,CAAC,EAAE,qBAAqB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpG,CAAC"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type LoadAppOptions, type LoadedAppFrontend } from "./frontend.js";
|
|
2
|
+
import type { AppExtension, AppScope } from "./extensions.js";
|
|
3
|
+
import type { InstalledApp } from "./apps.js";
|
|
4
|
+
import type { Agent, AgentConfig, AgentCoreEvent, AgentCreateInput, AgentCreateResult, AgentDeleteResult, AgentPauseResult, AgentRestartResult, AgentStatus, AgentSystemMCPResult, AgentUpdateInput, AptevaClientOptions, AuthStatus, ChannelInfo, Chat, CreateChatOptions, CreateOrResumeChatOptions, ChatCreateResult, CreateDelegatedUserInput, DelegatedUserToken, ChatHistoryMessage, ChatMessage, ChatMessagesQuery, ChatSendInput, ChatSendOptions, ChatStreamOptions, MCPCallResponse, Project, StreamHandle, SubscribeOptions, SSEEventMetadata, TelemetryEvent, TelemetryPeriod, TelemetryQuery, TelemetryStats, Thread, TimelineBucket, UpdateChatOptions, User } from "./types.js";
|
|
2
5
|
export declare class AptevaClient {
|
|
3
6
|
private readonly baseURL;
|
|
4
7
|
private apiKey?;
|
|
@@ -7,12 +10,18 @@ export declare class AptevaClient {
|
|
|
7
10
|
private readonly fetchImpl;
|
|
8
11
|
private readonly onUnauthorized?;
|
|
9
12
|
private readonly timeoutMs;
|
|
13
|
+
private readonly refreshTokenHook?;
|
|
14
|
+
private credentialRevision;
|
|
15
|
+
private pendingTokenRefresh?;
|
|
10
16
|
private readonly pendingChatResolutions;
|
|
11
17
|
constructor(opts: AptevaClientOptions);
|
|
12
18
|
setApiKey(key: string | undefined): void;
|
|
13
19
|
getApiKey(): string | undefined;
|
|
14
20
|
setAccessToken(token: string | undefined): void;
|
|
15
21
|
getAccessToken(): string | undefined;
|
|
22
|
+
/** Refresh once for concurrent callers. Undefined leaves credentials unchanged. */
|
|
23
|
+
refreshAccessToken(): Promise<boolean>;
|
|
24
|
+
private renewAfterUnauthorized;
|
|
16
25
|
readonly auth: {
|
|
17
26
|
register: (email: string, password: string, name?: string) => Promise<{
|
|
18
27
|
user: User;
|
|
@@ -61,7 +70,7 @@ export declare class AptevaClient {
|
|
|
61
70
|
togglePause: (id: number) => Promise<AgentPauseResult>;
|
|
62
71
|
event: <R = unknown>(id: number, body: Record<string, unknown>) => Promise<R>;
|
|
63
72
|
control: <R = unknown>(id: number, body: Record<string, unknown>) => Promise<R>;
|
|
64
|
-
events: <E = AgentCoreEvent>(id: number, onEvent: (event: E) => void, opts?: SubscribeOptions) => StreamHandle;
|
|
73
|
+
events: <E = AgentCoreEvent>(id: number, onEvent: (event: E, metadata: SSEEventMetadata) => void, opts?: SubscribeOptions) => StreamHandle;
|
|
65
74
|
};
|
|
66
75
|
readonly telemetry: {
|
|
67
76
|
query: (q: TelemetryQuery) => Promise<TelemetryEvent[]>;
|
|
@@ -86,15 +95,23 @@ export declare class AptevaClient {
|
|
|
86
95
|
last_seen_id: number;
|
|
87
96
|
}>;
|
|
88
97
|
};
|
|
89
|
-
app<T = unknown>(name: string): AppHandle<T>;
|
|
90
|
-
|
|
98
|
+
app<T = unknown>(name: string, scope?: AppScope): AppHandle<T>;
|
|
99
|
+
/** Create an extension instance. Retain it in the host; use() does not cache. */
|
|
100
|
+
use<T>(extension: AppExtension<T>, scope?: AppScope): T;
|
|
101
|
+
readonly apps: {
|
|
102
|
+
/** Load an installed app’s bundled client and optional React UI. */
|
|
103
|
+
load: <TClient = unknown, TComponent = unknown>(name: string, options: LoadAppOptions) => Promise<LoadedAppFrontend<TClient, TComponent>>;
|
|
104
|
+
/** Requires the platform's existing app-list permission. No auth bypass. */
|
|
105
|
+
list: (scope?: Pick<AppScope, "projectId">, init?: RequestInit) => Promise<InstalledApp[]>;
|
|
106
|
+
};
|
|
107
|
+
callTool<R>(appName: string, toolName: string, args: Record<string, unknown>, init?: RequestInit): Promise<R>;
|
|
91
108
|
get<R>(path: string, init?: RequestInit): Promise<R>;
|
|
92
109
|
post<R>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
93
110
|
put<R>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
94
111
|
patch<R>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
95
112
|
del<R>(path: string, init?: RequestInit): Promise<R>;
|
|
96
113
|
sseURL(path: string, params?: Record<string, string | number | undefined>): string;
|
|
97
|
-
subscribe<E>(path: string, params: Record<string, string | number | undefined> | undefined, onEvent: (event: E) => void, opts?: SubscribeOptions): StreamHandle;
|
|
114
|
+
subscribe<E>(path: string, params: Record<string, string | number | undefined> | undefined, onEvent: (event: E, metadata: SSEEventMetadata) => void, opts?: SubscribeOptions): StreamHandle;
|
|
98
115
|
private subscribeWithFetch;
|
|
99
116
|
private bindAbortSignal;
|
|
100
117
|
private appPath;
|
|
@@ -105,12 +122,15 @@ export declare class AptevaClient {
|
|
|
105
122
|
export declare function unwrapMCP<R>(env: MCPCallResponse<unknown>): R;
|
|
106
123
|
export interface AppHandle<TDefault = unknown> {
|
|
107
124
|
readonly name: string;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
readonly projectId?: string;
|
|
126
|
+
readonly installId?: number;
|
|
127
|
+
get<R = TDefault>(path: string, init?: RequestInit): Promise<R>;
|
|
128
|
+
post<R = TDefault>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
129
|
+
put<R = TDefault>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
130
|
+
patch<R = TDefault>(path: string, body?: unknown, init?: RequestInit): Promise<R>;
|
|
131
|
+
del<R = TDefault>(path: string, init?: RequestInit): Promise<R>;
|
|
132
|
+
tool<R = TDefault>(name: string, args?: Record<string, unknown>, init?: RequestInit): Promise<R>;
|
|
133
|
+
subscribe<E>(path: string, onEvent: (event: E, metadata: SSEEventMetadata) => void, opts?: SubscribeOptions): StreamHandle;
|
|
114
134
|
mcpURL(queryParams?: Record<string, string>): string;
|
|
115
135
|
}
|
|
116
136
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|