@skaile/workspaces 1.6.0 → 1.6.1
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 +64 -0
- package/dist/app-sdk/index.js +22 -7
- package/dist/app-sdk/index.js.map +1 -1
- package/dist/app-sdk/src/connection-params.d.ts +7 -0
- package/dist/app-sdk/src/connection-params.d.ts.map +1 -1
- package/dist/{asset-feeds-T35ELX2O.js → asset-feeds-PTZBWJEL.js} +11 -11
- package/dist/asset-feeds-PTZBWJEL.js.map +1 -0
- package/dist/asset-manager/index.js +6 -6
- package/dist/asset-manager/installer.js +4 -4
- package/dist/asset-manager/src/index.d.ts +18 -0
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/{chunk-HY33RMEA.js → chunk-5NZ56R63.js} +3 -3
- package/dist/{chunk-HY33RMEA.js.map → chunk-5NZ56R63.js.map} +1 -1
- package/dist/{chunk-ZBZQC5R7.js → chunk-7J7LQP6T.js} +3 -3
- package/dist/{chunk-ZBZQC5R7.js.map → chunk-7J7LQP6T.js.map} +1 -1
- package/dist/{chunk-RDYOU6N6.js → chunk-EA73UA42.js} +2 -2
- package/dist/{chunk-RDYOU6N6.js.map → chunk-EA73UA42.js.map} +1 -1
- package/dist/{chunk-BEO6KN7P.js → chunk-HIATVS2E.js} +5 -5
- package/dist/{chunk-BEO6KN7P.js.map → chunk-HIATVS2E.js.map} +1 -1
- package/dist/{chunk-UQKYT4V6.js → chunk-ISA7R7PK.js} +3 -3
- package/dist/{chunk-UQKYT4V6.js.map → chunk-ISA7R7PK.js.map} +1 -1
- package/dist/{chunk-CBA2XIBF.js → chunk-M4UKIJ5K.js} +8 -8
- package/dist/{chunk-CBA2XIBF.js.map → chunk-M4UKIJ5K.js.map} +1 -1
- package/dist/{chunk-FDE7XRQA.js → chunk-MWE7DEGC.js} +3 -3
- package/dist/{chunk-FDE7XRQA.js.map → chunk-MWE7DEGC.js.map} +1 -1
- package/dist/{chunk-3CHARYD4.js → chunk-MY7TSVO7.js} +95 -28
- package/dist/chunk-MY7TSVO7.js.map +1 -0
- package/dist/{chunk-DMG6NJH7.js → chunk-NRWDQBZ7.js} +4 -4
- package/dist/{chunk-DMG6NJH7.js.map → chunk-NRWDQBZ7.js.map} +1 -1
- package/dist/{chunk-WYGXYGJH.js → chunk-NTYCGPZ3.js} +9 -9
- package/dist/{chunk-WYGXYGJH.js.map → chunk-NTYCGPZ3.js.map} +1 -1
- package/dist/{chunk-KEQ4GLVM.js → chunk-OJ7G56I7.js} +3 -3
- package/dist/{chunk-KEQ4GLVM.js.map → chunk-OJ7G56I7.js.map} +1 -1
- package/dist/{chunk-2DWXGUZA.js → chunk-QHU6TM7D.js} +2 -2
- package/dist/{chunk-2DWXGUZA.js.map → chunk-QHU6TM7D.js.map} +1 -1
- package/dist/{chunk-KMY7HVQ6.js → chunk-SH55Z5QA.js} +3 -3
- package/dist/{chunk-KMY7HVQ6.js.map → chunk-SH55Z5QA.js.map} +1 -1
- package/dist/{chunk-JIDKX7GC.js → chunk-SNN6O7ON.js} +4 -4
- package/dist/{chunk-JIDKX7GC.js.map → chunk-SNN6O7ON.js.map} +1 -1
- package/dist/{chunk-6NR2BEIY.js → chunk-STQOPWPS.js} +4 -4
- package/dist/{chunk-6NR2BEIY.js.map → chunk-STQOPWPS.js.map} +1 -1
- package/dist/{chunk-2ZMO4CFG.js → chunk-VK24PIAB.js} +5 -4
- package/dist/chunk-VK24PIAB.js.map +1 -0
- package/dist/{chunk-RSYV7XEN.js → chunk-X7G2CCW4.js} +4 -4
- package/dist/{chunk-RSYV7XEN.js.map → chunk-X7G2CCW4.js.map} +1 -1
- package/dist/{chunk-J5TI55E7.js → chunk-Y5B5272E.js} +23 -7
- package/dist/chunk-Y5B5272E.js.map +1 -0
- package/dist/cli/index.js +546 -180
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/asset-feeds.d.ts.map +1 -1
- package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-index.d.ts +49 -0
- package/dist/cli/src/commands/library-index.d.ts.map +1 -0
- package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
- package/dist/cli/src/commands/manage.d.ts +22 -1
- package/dist/cli/src/commands/manage.d.ts.map +1 -1
- package/dist/cli/src/paths.d.ts.map +1 -1
- package/dist/cli/src/store-auth.d.ts.map +1 -1
- package/dist/cli/src/store-client.d.ts.map +1 -1
- package/dist/connectors/config.js +4 -4
- package/dist/connectors/index.js +5 -5
- package/dist/core/index.js +3 -3
- package/dist/core/runtime-assets.js +2 -2
- package/dist/core/src/index.d.ts +1 -1
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/repo-manager.d.ts +4 -0
- package/dist/core/src/repo-manager.d.ts.map +1 -1
- package/dist/core/workspace-config.js +1 -1
- package/dist/deploy/index.js +3 -3
- package/dist/{ensure-sources-QDVFUIUP.js → ensure-sources-ORACQA4I.js} +9 -9
- package/dist/{ensure-sources-QDVFUIUP.js.map → ensure-sources-ORACQA4I.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +5 -5
- package/dist/factory-assets/connectors/deploy.js +5 -5
- package/dist/factory-assets/connectors/devserver.js +5 -5
- package/dist/factory-assets/connectors/flow/adapter.js +5 -5
- package/dist/factory-assets/connectors/flow/engine.js +3 -3
- package/dist/factory-assets/connectors/flow/run-flow.js +6 -6
- package/dist/factory-assets/connectors/flow.js +5 -5
- package/dist/factory-assets/connectors/git.js +5 -5
- package/dist/factory-assets/connectors/gmail.js +5 -5
- package/dist/factory-assets/connectors/googledrive.js +5 -5
- package/dist/factory-assets/connectors/local.js +5 -5
- package/dist/factory-assets/connectors/mattermost.js +5 -5
- package/dist/factory-assets/connectors/memory.js +5 -5
- package/dist/factory-assets/connectors/minio.js +5 -5
- package/dist/factory-assets/connectors/postgres.js +5 -5
- package/dist/factory-assets/connectors/s3.js +5 -5
- package/dist/factory-assets/connectors/sharepoint.js +5 -5
- package/dist/factory-assets/connectors/sqlite.js +5 -5
- package/dist/factory-assets/connectors/static-server.js +5 -5
- package/dist/factory-assets/connectors/tunnel.js +5 -5
- package/dist/factory-assets/connectors/webdav.js +5 -5
- package/dist/factory-assets/connectors/xstate-store.js +5 -5
- package/dist/factory-assets/connectors/xstate.js +5 -5
- package/dist/{flows-MRVXTYWC.js → flows-RR6R3WBL.js} +3 -3
- package/dist/{flows-MRVXTYWC.js.map → flows-RR6R3WBL.js.map} +1 -1
- package/dist/{helpers-TF7GCBRQ.js → helpers-SXGIHFCF.js} +9 -9
- package/dist/{helpers-TF7GCBRQ.js.map → helpers-SXGIHFCF.js.map} +1 -1
- package/dist/library/index.js +4 -4
- package/dist/library/install.js +3 -3
- package/dist/{library-publish-K5IBE44D.js → library-publish-NQQFPOUY.js} +27 -19
- package/dist/library-publish-NQQFPOUY.js.map +1 -0
- package/dist/{open-library-4HMIDK3N.js → open-library-ODW2PJ2J.js} +10 -10
- package/dist/{open-library-4HMIDK3N.js.map → open-library-ODW2PJ2J.js.map} +1 -1
- package/dist/paths-KOKCMBL5.js +21 -0
- package/dist/{paths-P3DWFUPS.js.map → paths-KOKCMBL5.js.map} +1 -1
- package/dist/{plugin-store-S7OMGI7N.js → plugin-store-QMJ2B3VM.js} +5 -5
- package/dist/{plugin-store-S7OMGI7N.js.map → plugin-store-QMJ2B3VM.js.map} +1 -1
- package/dist/runner/index.js +7 -7
- package/dist/sdk/asset-manager.js +6 -6
- package/dist/sdk/core.js +3 -3
- package/dist/sdk/flow.js +3 -3
- package/dist/sdk/index.js +7 -7
- package/dist/sdk/runner.js +7 -7
- package/dist/{setup-2CCS63L6.js → setup-ETGRXSZG.js} +5 -5
- package/dist/{setup-2CCS63L6.js.map → setup-ETGRXSZG.js.map} +1 -1
- package/dist/{source-LOAM7IIQ.js → source-KPH66TXY.js} +11 -11
- package/dist/{source-LOAM7IIQ.js.map → source-KPH66TXY.js.map} +1 -1
- package/dist/{source-manifest-publish-TGVZ6KMC.js → source-manifest-publish-HJ5G4CTH.js} +13 -13
- package/dist/{source-manifest-publish-TGVZ6KMC.js.map → source-manifest-publish-HJ5G4CTH.js.map} +1 -1
- package/dist/tui/index.js +7 -7
- package/dist/workspace-plugin/adapters/mcp.js +1 -1
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +1 -1
- package/dist/asset-feeds-T35ELX2O.js.map +0 -1
- package/dist/chunk-2ZMO4CFG.js.map +0 -1
- package/dist/chunk-3CHARYD4.js.map +0 -1
- package/dist/chunk-J5TI55E7.js.map +0 -1
- package/dist/library-publish-K5IBE44D.js.map +0 -1
- package/dist/paths-P3DWFUPS.js +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#354](https://github.com/skaile-ai/workspaces/pull/354) [`2e88f72`](https://github.com/skaile-ai/workspaces/commit/2e88f722b4b2df4888601ebfa3cf635264a004e7) Thanks [@henkbla](https://github.com/henkbla)! - fix(app-sdk): re-send the ready ping until the host answers
|
|
8
|
+
|
|
9
|
+
`resolveAppConnectionParams` posted `skaile_app_ready` exactly once and then waited
|
|
10
|
+
out its timeout. The host only mints a connection-param ticket in response to a ping
|
|
11
|
+
and never speaks unprompted, so an app that booted before the host's own state had
|
|
12
|
+
settled lost the race permanently: its single ping was dropped, nothing re-asked, and
|
|
13
|
+
the app failed with `timed out after 10000ms waiting for connection params from the
|
|
14
|
+
host` against a healthy, minting host.
|
|
15
|
+
|
|
16
|
+
The ping now repeats until answered, backing off 100 ms → 1 s within the same
|
|
17
|
+
`timeoutMs` deadline (the host mints a single-use ticket per answered ping, so the
|
|
18
|
+
backoff keeps an unlucky boot from churning tickets). Behaviour is unchanged when the
|
|
19
|
+
host is already ready — still one ping, one ticket.
|
|
20
|
+
|
|
21
|
+
- [#347](https://github.com/skaile-ai/workspaces/pull/347) [`42f1285`](https://github.com/skaile-ai/workspaces/commit/42f1285b145aa055d2956c753461e4d71c2567c1) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Make locally-authored library assets consumable without publishing. `skaile add` /
|
|
22
|
+
`skaile install` now fall back to the user's registered libraries when the
|
|
23
|
+
source/store pass misses: each library is scanned on disk under its declared
|
|
24
|
+
publisher and a `kind:@publisher/name` match is deployed through the normal copy
|
|
25
|
+
pipeline and recorded as a dependency (never written to the lock — a local
|
|
26
|
+
authoring place is not a reproducible source). Version pins are honoured with the
|
|
27
|
+
same `matchPin` SemVer-range matcher the source resolver uses.
|
|
28
|
+
|
|
29
|
+
`skaile asset install` / `asset show` additionally normalize the `kind:@pub/name`
|
|
30
|
+
wire ref (with or without a `#version` range/exact/SHA pin) instead of requiring
|
|
31
|
+
the fully-canonical stored id, and `asset install --from <library>` now scopes
|
|
32
|
+
resolution to that library rather than only warning on a mismatch. Closes the
|
|
33
|
+
`library create` → `add` loop end-to-end.
|
|
34
|
+
|
|
35
|
+
`coerceVersion` and `matchPin` are now exported from `@skaile/workspaces/core`.
|
|
36
|
+
|
|
37
|
+
- [#350](https://github.com/skaile-ai/workspaces/pull/350) [`c786467`](https://github.com/skaile-ai/workspaces/commit/c7864673bcdb29e013649c8fb93ac0d4691064ee) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Fix two gaps that blocked the `library create → publish` round-trip:
|
|
38
|
+
|
|
39
|
+
- `library publish --create-repo --dry-run` no longer actually creates a GitHub
|
|
40
|
+
repo — the `gh repo create` step was running before the dry-run guard. A
|
|
41
|
+
dry-run now just shows the would-be remote.
|
|
42
|
+
- `library create` now registers the scaffolded asset in the manifest `assets[]`
|
|
43
|
+
(the inventory `library publish` filters by), so a freshly-created asset is
|
|
44
|
+
actually publishable instead of yielding "0 assets". `library index` reconciles
|
|
45
|
+
the inventory with disk, so a hand-added asset (created outside `library
|
|
46
|
+
create`) becomes publishable too — without dropping curated entries.
|
|
47
|
+
|
|
48
|
+
- [#347](https://github.com/skaile-ai/workspaces/pull/347) [`42f1285`](https://github.com/skaile-ai/workspaces/commit/42f1285b145aa055d2956c753461e4d71c2567c1) Thanks [@P1umPudding](https://github.com/P1umPudding)! - `skaile manage` TUI: make library assets first-class in the browser.
|
|
49
|
+
|
|
50
|
+
- **Libraries tab** is now an expandable, installable drill-in tree (`Libraries → library → domain → asset`) mirroring the Sources tab — expand/collapse with `←/→`, bulk-select a whole library from its header, install/remove per asset — instead of a read-only management list.
|
|
51
|
+
- **Assets tab**: library assets group under a clean `<library-name>` band with no `library:` prefix and no redundant same-named domain level (previously `libraries > library:my-lib > library:my-lib > greet`).
|
|
52
|
+
- **Assets tab**: every origin band except the built-in `factory` now default-collapses (one-shot, a user expand survives reloads), so a project with many sources/libraries opens compact.
|
|
53
|
+
|
|
54
|
+
- [#349](https://github.com/skaile-ai/workspaces/pull/349) [`64eeaa4`](https://github.com/skaile-ai/workspaces/commit/64eeaa4a3101e31934ec19d554a4c7dd44eef261) Thanks [@P1umPudding](https://github.com/P1umPudding)! - `skaile store login`: implement PKCE (RFC 7636, S256) in the Keycloak device
|
|
55
|
+
flow. The `skaile-store-cli` client enforces PKCE, so the device-authorization
|
|
56
|
+
request now sends a `code_challenge` + `code_challenge_method=S256` and the token
|
|
57
|
+
poll sends the matching `code_verifier`. Without it Keycloak rejected the request
|
|
58
|
+
with `invalid_request: Missing parameter: code_challenge_method`.
|
|
59
|
+
|
|
60
|
+
- [#352](https://github.com/skaile-ai/workspaces/pull/352) [`b8c30ce`](https://github.com/skaile-ai/workspaces/commit/b8c30ce9ad597244264c30a61a07819a1325503b) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Surface the store's human-readable error on `library publish` / `register` /
|
|
61
|
+
`yank`. The store returns `{ error, message }` on failures; the CLI threw the
|
|
62
|
+
bare `error` code (`UNPROCESSABLE_CONTENT`) and — because the publish commands
|
|
63
|
+
didn't catch it — crashed with a raw stack trace. Now `storeFetch` prefers the
|
|
64
|
+
actionable `message` (e.g. "Namespace '@x' is reserved for the GitHub identity …
|
|
65
|
+
Sign in with GitHub to claim it") and the commands print it cleanly via `logErr`.
|
|
66
|
+
|
|
3
67
|
## 1.6.0
|
|
4
68
|
|
|
5
69
|
### Minor Changes
|
package/dist/app-sdk/index.js
CHANGED
|
@@ -290,6 +290,9 @@ function createSkaileApp(options) {
|
|
|
290
290
|
// app-sdk/src/connection-params.ts
|
|
291
291
|
var PARENT_ORIGIN_QUERY_PARAM = "skaileParentOrigin";
|
|
292
292
|
var DEFAULT_PARAMS_TIMEOUT_MS = 1e4;
|
|
293
|
+
var INITIAL_PING_INTERVAL_MS = 100;
|
|
294
|
+
var MAX_PING_INTERVAL_MS = 1e3;
|
|
295
|
+
var PING_BACKOFF_FACTOR = 2;
|
|
293
296
|
function readParentOriginFromQuery(search) {
|
|
294
297
|
const value = new URLSearchParams(search).get(PARENT_ORIGIN_QUERY_PARAM);
|
|
295
298
|
return value && value.length > 0 ? value : null;
|
|
@@ -324,10 +327,15 @@ function resolveAppConnectionParams(options) {
|
|
|
324
327
|
if (parentOrigin === "*" || parentOrigin === "null") {
|
|
325
328
|
return Promise.reject(new Error(`refusing to use an unpinnable host origin: ${parentOrigin}`));
|
|
326
329
|
}
|
|
330
|
+
const peer = parent;
|
|
331
|
+
const peerOrigin = parentOrigin;
|
|
327
332
|
return new Promise((resolve, reject) => {
|
|
328
333
|
let settled = false;
|
|
334
|
+
let pingTimer;
|
|
335
|
+
let pingInterval = INITIAL_PING_INTERVAL_MS;
|
|
329
336
|
const cleanup = () => {
|
|
330
337
|
clearTimeout(timer);
|
|
338
|
+
clearTimeout(pingTimer);
|
|
331
339
|
win.removeEventListener("message", onMessage);
|
|
332
340
|
};
|
|
333
341
|
const timer = setTimeout(() => {
|
|
@@ -340,7 +348,7 @@ function resolveAppConnectionParams(options) {
|
|
|
340
348
|
}, timeoutMs);
|
|
341
349
|
function onMessage(event) {
|
|
342
350
|
if (settled) return;
|
|
343
|
-
if (event.origin !==
|
|
351
|
+
if (event.origin !== peerOrigin || event.source !== peer) return;
|
|
344
352
|
if (!isParamsMessage(event.data, appId)) return;
|
|
345
353
|
settled = true;
|
|
346
354
|
cleanup();
|
|
@@ -348,13 +356,20 @@ function resolveAppConnectionParams(options) {
|
|
|
348
356
|
}
|
|
349
357
|
win.addEventListener("message", onMessage);
|
|
350
358
|
const ready = { type: "skaile_app_ready", appId };
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
359
|
+
function ping() {
|
|
360
|
+
if (settled) return;
|
|
361
|
+
try {
|
|
362
|
+
peer.postMessage(ready, peerOrigin);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
settled = true;
|
|
365
|
+
cleanup();
|
|
366
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
pingTimer = setTimeout(ping, pingInterval);
|
|
370
|
+
pingInterval = Math.min(pingInterval * PING_BACKOFF_FACTOR, MAX_PING_INTERVAL_MS);
|
|
357
371
|
}
|
|
372
|
+
ping();
|
|
358
373
|
});
|
|
359
374
|
}
|
|
360
375
|
function defineAppCapability(args) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../app-sdk/src/logger.ts","../../app-sdk/src/app.ts","../../app-sdk/src/connection-params.ts","../../app-sdk/src/define-app-capability.ts","../../app-sdk/src/ws-transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal logger for the app SDK.\n *\n * The SDK is meant to run inside an embedded app (browser or a lightweight\n * Node service), so it deliberately does not pull in the framework's\n * `LogStore` / SQLite logging stack. Authors can pass their own {@link AppLogger}\n * (e.g. a bridge to their app's telemetry) or accept the console-backed default.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\n/** Structured leveled logger the SDK and capability handlers emit through. */\nexport interface AppLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n}\n\n/** A logger that discards everything. The default when none is supplied. */\nexport const noopLogger: AppLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n};\n\n/**\n * Console-backed logger, prefixing every line with `[skaile-app]`. Opt in by\n * passing `logger: consoleLogger` to {@link createSkaileApp} during development.\n */\nexport const consoleLogger: AppLogger = {\n debug(message, data) {\n console.debug(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n info(message, data) {\n console.info(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n warn(message, data) {\n console.warn(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n error(message, data) {\n console.error(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n};\n","/**\n * `createSkaileApp` — the thin app SDK that makes an embedded app\n * agent-controllable.\n *\n * An app becomes a *capability peer*, mirroring how the runner behaves toward\n * the platform:\n *\n * 1. Opens an authenticated back-connection (via an {@link AppTransport}).\n * 2. Performs the app handshake — sends `app_hello`, awaits `app_hello_ack`\n * (binding the connection to `session + appId`) or `app_hello_nack` (refusal).\n * 3. Registers its `app.*` capabilities via `capability_register`, stamping\n * each with `origin:{kind:'app', appId}` and `side:'app'`. Register /\n * deregister mid-session is supported.\n * 4. Answers inbound `capability_invoked` events by running the local handler\n * and replying with `capability_result` (a thrown handler rides back as\n * `{ error }`).\n *\n * The approval round-trip for `requiresApproval` capabilities is handled\n * entirely platform-side: the gateway parks the invocation for user approval\n * and only forwards `capability_invoked` to the app *after* approval, so the\n * app just marks the capability and answers when asked.\n *\n * The peer reconnects and replays its registrations on a dropped socket, and on\n * a *retryable* refusal — the app boots independently of the session's agent\n * container and can win that race. See {@link ReconnectOptions}.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { Capability } from \"@skaile/workspaces/types\";\nimport { PROTOCOL_VERSION_STRING } from \"@skaile/workspaces/types\";\nimport type { DefinedAppCapability } from \"./define-app-capability.js\";\nimport { type AppLogger, noopLogger } from \"./logger.js\";\nimport type { AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Default timeout waiting for the gateway's `app_hello_ack`. */\nexport const DEFAULT_HELLO_TIMEOUT_MS = 10_000;\n\n/** Defaults mirroring the agent `WebSocketClientTransport`'s backoff. */\nconst DEFAULT_RECONNECT: Required<ReconnectOptions> = {\n maxAttempts: 5,\n baseMs: 1_000,\n maxMs: 30_000,\n};\n\n/**\n * Reconnect / retry policy. Set `reconnect: false` on {@link SkaileAppOptions}\n * to disable entirely (one attempt, no replay).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ReconnectOptions {\n /** Attempts before giving up. Default 5. */\n maxAttempts?: number;\n /** First backoff delay in ms; doubles each attempt. Default 1000. */\n baseMs?: number;\n /** Backoff ceiling in ms. Default 30000. */\n maxMs?: number;\n}\n\n/**\n * The gateway refused the handshake. `retryable` mirrors the wire field: `true`\n * for a transient refusal (the agent container is not up yet), `false` for a\n * permanent one (bad token, `appId` not permitted).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport class AppHandshakeRefusedError extends Error {\n readonly retryable: boolean;\n constructor(reason: string, retryable: boolean) {\n super(`app handshake refused: ${reason}`);\n this.name = \"AppHandshakeRefusedError\";\n this.retryable = retryable;\n }\n}\n\n/**\n * Options for {@link createSkaileApp}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileAppOptions {\n /** The embedded app's stable id. Every registered capability is bound to it. */\n appId: string;\n /**\n * The app back-connection. Inject {@link createWebSocketAppTransport} in\n * production or a fake in tests. The transport carries the injected session\n * identity as its connection credential.\n *\n * With `reconnect` enabled the transport must be able to produce a **fresh**\n * token per attempt — the gateway consumes it at `app_hello`. Pass a\n * `SessionTokenProvider` rather than a bare string.\n */\n transport: AppTransport;\n /** Protocol version the SDK announces in `app_hello`. Defaults to the SDK's. */\n protocolVersion?: string;\n /** Timeout waiting for `app_hello_ack`. Defaults to {@link DEFAULT_HELLO_TIMEOUT_MS}. */\n helloTimeoutMs?: number;\n /** Logger for the SDK and (by default) capability handlers. Defaults to {@link noopLogger}. */\n logger?: AppLogger;\n /**\n * Reconnect policy, or `false` to disable. Defaults to\n * `{maxAttempts:5, baseMs:1000, maxMs:30000}`.\n *\n * @since 3.5.0\n */\n reconnect?: ReconnectOptions | false;\n}\n\n/** Result of a successful handshake — the bound session context. */\nexport interface AppSession {\n sessionId: string;\n userId?: string;\n}\n\n/**\n * A connected (or connectable) Skaile app. Register capabilities before or\n * after {@link connect}; post-connect changes are pushed to the gateway\n * immediately.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileApp {\n /**\n * Add (or replace, by name) an app capability. Registering the same name\n * again replaces it — mirrors the registry's replace-by-name semantics. When\n * already connected, a `capability_register` is sent immediately.\n */\n registerCapability(cap: DefinedAppCapability): void;\n /**\n * Remove a capability by name. No-op when unknown. When connected, a\n * `capability_deregister` is sent immediately so the tool disappears from the\n * agent's catalog.\n */\n deregisterCapability(name: string): void;\n /** Wire-format snapshot of the currently registered capabilities (origin stamped). */\n readonly capabilities: readonly Capability[];\n /** The bound session once connected; `null` before the handshake and while reconnecting. */\n readonly session: AppSession | null;\n /**\n * Open the connection, perform the handshake, and register the current\n * capabilities. Resolves with the bound {@link AppSession}. Rejects if the\n * gateway refuses permanently, the handshake times out, or the transport\n * disconnects mid-handshake. A *retryable* refusal is retried per the\n * reconnect policy before rejecting.\n */\n connect(): Promise<AppSession>;\n /**\n * Subscribe to session binding changes. Fires on **every** bind — including\n * the initial `connect()` — with the new {@link AppSession}, and with `null`\n * whenever the connection drops. Returns an unsubscribe function.\n *\n * @since 3.5.0\n */\n onSessionChange(handler: (session: AppSession | null) => void): () => void;\n /** Disconnect and tear down. Idempotent. Cancels any pending reconnect. */\n close(): Promise<void>;\n}\n\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * Create a {@link SkaileApp}. See the module doc for the peer lifecycle.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createSkaileApp(options: SkaileAppOptions): SkaileApp {\n const {\n appId,\n transport,\n protocolVersion = PROTOCOL_VERSION_STRING,\n helloTimeoutMs = DEFAULT_HELLO_TIMEOUT_MS,\n logger = noopLogger,\n } = options;\n\n const reconnectEnabled = options.reconnect !== false;\n const policy: Required<ReconnectOptions> = {\n ...DEFAULT_RECONNECT,\n ...(options.reconnect === false ? {} : options.reconnect),\n };\n\n const registry = new Map<string, DefinedAppCapability>();\n const sessionListeners = new Set<(s: AppSession | null) => void>();\n let session: AppSession | null = null;\n let closed = false;\n let connecting = false;\n let subscribed = false;\n // Bumped by close(). A reconnect loop parked on its backoff timer captures the\n // epoch it started in and abandons if close() (or a close/connect cycle) has\n // since happened — otherwise it would resurrect a torn-down app.\n let epoch = 0;\n let unsubscribeMessage: (() => void) | null = null;\n let unsubscribeDisconnect: (() => void) | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n /** Resolver of the backoff sleep, so close() can wake a parked loop. */\n let reconnectWake: (() => void) | null = null;\n\n /** Non-null only while a handshake is in flight. */\n let pending: { settle: (err: Error | null, s?: AppSession) => void } | null = null;\n\n function toWire(cap: DefinedAppCapability): Capability {\n return {\n name: cap.name,\n displayName: cap.displayName,\n description: cap.description,\n inputSchema: cap.inputSchema,\n outputSchema: cap.outputSchema,\n side: \"app\",\n origin: { kind: \"app\", appId },\n scope: cap.scope,\n requiresApproval: cap.requiresApproval,\n kind: cap.kind,\n audience: cap.audience,\n promptFragment: cap.promptFragment,\n callTimeoutMs: cap.callTimeoutMs,\n render: cap.render,\n };\n }\n\n function setSession(next: AppSession | null): void {\n session = next;\n for (const h of sessionListeners) h(next);\n }\n\n function registerCapability(cap: DefinedAppCapability): void {\n registry.set(cap.name, cap);\n if (session) {\n transport.send({ type: \"capability_register\", capabilities: [toWire(cap)] });\n logger.debug(\"registered capability (live)\", { name: cap.name });\n }\n }\n\n function deregisterCapability(name: string): void {\n if (!registry.delete(name)) return;\n if (session) {\n transport.send({ type: \"capability_deregister\", names: [name] });\n logger.debug(\"deregistered capability (live)\", { name });\n }\n }\n\n /** Dispatch an inbound invocation to the local handler; reply with the result. */\n async function handleInvocation(callId: string, name: string, input: unknown): Promise<void> {\n const cap = registry.get(name);\n if (!cap) {\n transport.send({\n type: \"capability_result\",\n callId,\n result: { error: `Unknown app capability: ${name}` },\n });\n return;\n }\n try {\n const validated = cap.inputZod.parse(input);\n const ctx = { sessionId: session?.sessionId ?? \"\", appId, log: logger };\n const result = await cap.handler(validated, ctx);\n transport.send({ type: \"capability_result\", callId, result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\"capability handler failed\", { name, error: message });\n transport.send({ type: \"capability_result\", callId, result: { error: message } });\n }\n }\n\n function onMessage(message: AppServerMessage): void {\n switch (message.type) {\n case \"app_hello_ack\": {\n const bound: AppSession = { sessionId: message.sessionId, userId: message.userId };\n pending?.settle(null, bound);\n return;\n }\n case \"app_hello_nack\":\n pending?.settle(new AppHandshakeRefusedError(message.error, message.retryable === true));\n return;\n case \"capability_invoked\":\n void handleInvocation(message.callId, message.name, message.input);\n return;\n }\n }\n\n function onDisconnect(): void {\n if (pending) {\n pending.settle(new Error(\"transport disconnected during handshake\"));\n return;\n }\n if (closed || !session) return;\n logger.warn(\"app connection lost\", { appId });\n setSession(null);\n if (!reconnectEnabled) return;\n // The loop holds the same guard connect() uses: a manual connect() while it\n // runs would start a second handshake, and both would overwrite `pending`.\n connecting = true;\n const loopEpoch = epoch;\n void reconnectLoop().finally(() => {\n // A close()/connect() cycle since this loop started owns the guard now.\n if (epoch === loopEpoch) connecting = false;\n });\n }\n\n function ensureSubscribed(): void {\n if (subscribed) return;\n unsubscribeMessage = transport.onMessage(onMessage);\n unsubscribeDisconnect = transport.onDisconnect(onDisconnect);\n subscribed = true;\n }\n\n /** One attempt: open the socket, exchange hello/ack, replay the registry. */\n function performHandshake(): Promise<AppSession> {\n ensureSubscribed();\n return transport.connect().then(\n () =>\n new Promise<AppSession>((resolve, reject) => {\n if (closed) {\n reject(new Error(\"app closed during handshake\"));\n return;\n }\n let settled = false;\n const timer = setTimeout(() => {\n pending?.settle(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));\n }, helloTimeoutMs);\n\n pending = {\n settle: (err, bound) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n pending = null;\n if (err || !bound) {\n reject(err ?? new Error(\"app handshake failed\"));\n return;\n }\n setSession(bound);\n logger.info(\"app connection established\", { appId, sessionId: bound.sessionId });\n // Replay the full surface — on a reconnect the gateway dropped our\n // caps when the previous connection closed.\n const wire = Array.from(registry.values()).map(toWire);\n if (wire.length > 0) {\n transport.send({ type: \"capability_register\", capabilities: wire });\n }\n resolve(bound);\n },\n };\n\n transport.send({ type: \"app_hello\", appId, protocolVersion });\n }),\n (err) => Promise.reject(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n const backoffMs = (attempt: number) =>\n Math.min(policy.maxMs, policy.baseMs * 2 ** Math.max(0, attempt - 1));\n\n /** Rebind after a dropped socket. Stops on a permanent refusal or close(). */\n async function reconnectLoop(): Promise<void> {\n const myEpoch = epoch;\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {\n const wait = backoffMs(attempt);\n logger.debug(\"scheduling app reconnect\", { appId, attempt, waitMs: wait });\n // close() cancels the timer, so it also has to hand us the resolver —\n // otherwise this await never settles and the loop frame parks forever,\n // pinning the transport and every other captured binding.\n await new Promise<void>((r) => {\n reconnectWake = r;\n reconnectTimer = setTimeout(r, wait);\n });\n reconnectWake = null;\n if (closed || epoch !== myEpoch) {\n logger.debug(\"app reconnect cancelled\", { appId, attempt });\n return;\n }\n try {\n await performHandshake();\n logger.info(\"app connection re-established\", { appId });\n return;\n } catch (err) {\n if (err instanceof AppHandshakeRefusedError && !err.retryable) {\n logger.error(\"app reconnect refused permanently\", { appId, error: err.message });\n return;\n }\n logger.warn(\"app reconnect attempt failed\", {\n appId,\n attempt,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n logger.error(\"app reconnect gave up\", { appId, attempts: policy.maxAttempts });\n }\n\n async function connect(): Promise<AppSession> {\n if (session) return session;\n if (connecting) throw new Error(\"app connect() is already in progress\");\n connecting = true;\n // A closed app is reusable: connect() reopens it. Any reconnect loop still\n // parked from the previous life is fenced off by the epoch close() bumped.\n closed = false;\n try {\n for (let attempt = 1; ; attempt++) {\n try {\n return await performHandshake();\n } catch (err) {\n // Only a *retryable* refusal is worth another attempt on the initial\n // connect: the app raced the session's agent container on boot. Every\n // other failure (bad token, timeout, transport error) is terminal here.\n const transient = err instanceof AppHandshakeRefusedError && err.retryable;\n if (!transient || !reconnectEnabled || attempt >= policy.maxAttempts || closed) {\n throw err;\n }\n logger.debug(\"app handshake refused (retryable), retrying\", { appId, attempt });\n await delay(backoffMs(attempt));\n }\n }\n } finally {\n connecting = false;\n }\n }\n\n async function close(): Promise<void> {\n closed = true;\n epoch++;\n clearTimeout(reconnectTimer);\n // Wake a loop parked on its backoff so it observes `closed` and returns,\n // rather than awaiting a timer that will now never fire.\n reconnectWake?.();\n reconnectWake = null;\n connecting = false;\n // Abort an in-flight handshake — rejects the pending connect and clears its\n // timer so nothing leaks past close().\n pending?.settle(new Error(\"app closed during handshake\"));\n unsubscribeMessage?.();\n unsubscribeDisconnect?.();\n unsubscribeMessage = null;\n unsubscribeDisconnect = null;\n subscribed = false;\n session = null;\n await transport.disconnect();\n }\n\n return {\n registerCapability,\n deregisterCapability,\n get capabilities() {\n return Array.from(registry.values()).map(toWire);\n },\n get session() {\n return session;\n },\n connect,\n onSessionChange(handler) {\n sessionListeners.add(handler);\n return () => sessionListeners.delete(handler);\n },\n close,\n };\n}\n","/**\n * Connection-param handshake between the embedding host and the app frame.\n *\n * An embedded app needs three things to open its back-connection: the gateway\n * URL, its own `appId`, and a session identity token. The token is a **secret**,\n * so it must not ride in the iframe `src` — the preview surface that serves the\n * app is unauthenticated, and a URL lands in access logs, `Referer` headers, and\n * browser history. Instead the host posts it over `postMessage`, and the app\n * asks for it with a ready ping once its script is running.\n *\n * The host cannot simply post on iframe `load`: it has no way to know when the\n * app's script is listening. So the app speaks first.\n *\n * ```text\n * app → host { type: 'skaile_app_ready', appId } targetOrigin = parentOrigin\n * host → app { type: 'skaile_app_params', appId, url, token } targetOrigin = appOrigin\n * ```\n *\n * Both sides pin the peer: the app checks `event.origin === parentOrigin` and\n * that the message came from its own parent; the host checks the origin and that\n * `event.source` is the very iframe it rendered. The app learns the expected\n * parent origin from a **non-secret** query param on its `src`\n * ({@link PARENT_ORIGIN_QUERY_PARAM}), which is safe to leak precisely because it\n * is not a credential.\n *\n * @category App SDK\n * @since 3.5.0\n */\n\n/**\n * Query param on the app's iframe `src` carrying the embedder's origin. Not a\n * secret — it is the value the app pins its `postMessage` peer to.\n */\nexport const PARENT_ORIGIN_QUERY_PARAM = \"skaileParentOrigin\";\n\n/** Default timeout waiting for the host's params reply. */\nexport const DEFAULT_PARAMS_TIMEOUT_MS = 10_000;\n\n/** App → host. Announces the app frame is listening and wants its params. */\nexport type SkaileAppReadyMessage = {\n type: \"skaile_app_ready\";\n /** The app asking. The host answers only for an app it actually embedded. */\n appId: string;\n};\n\n/** Host → app. Carries the gateway URL and the single-use session identity token. */\nexport type SkaileAppParamsMessage = {\n type: \"skaile_app_params\";\n /** Echoed back so a frame embedding several apps can disambiguate. */\n appId: string;\n /** The gateway app-channel URL, e.g. `wss://platform/app-connect`. */\n url: string;\n /** Single-use session identity token. Consumed by the gateway at `app_hello`. */\n token: string;\n};\n\n/** Resolved params, ready to hand to `createWebSocketAppTransport` + `createSkaileApp`. */\nexport type AppConnectionParams = {\n url: string;\n appId: string;\n token: string;\n};\n\n/** The subset of `postMessage` targets this module needs. */\nexport type PostMessageTarget = {\n postMessage(message: unknown, targetOrigin: string): void;\n};\n\n/** Structural view of a `MessageEvent`, so tests need no DOM. */\nexport type MessageEventLike = {\n readonly origin: string;\n readonly source: unknown;\n readonly data: unknown;\n};\n\n/** Structural view of the `window` bits this module needs. Injectable for tests. */\nexport type AppWindowLike = {\n readonly parent: PostMessageTarget | null;\n readonly location: { readonly search: string };\n addEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n removeEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n};\n\n/**\n * Options for {@link resolveAppConnectionParams}.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ResolveAppConnectionParamsOptions {\n /** The embedded app's stable id. Must match the params the host replies with. */\n appId: string;\n /**\n * Expected origin of the embedding host. Defaults to the\n * {@link PARENT_ORIGIN_QUERY_PARAM} query param on the current URL. Never `\"*\"`\n * or `\"null\"` — an unpinned peer defeats the point of not putting the token in\n * the URL.\n */\n parentOrigin?: string;\n /** How long to wait for the host's reply. Defaults to {@link DEFAULT_PARAMS_TIMEOUT_MS}. */\n timeoutMs?: number;\n /** Window to listen and post on. Defaults to the global. Injectable for tests. */\n windowRef?: AppWindowLike;\n}\n\nfunction readParentOriginFromQuery(search: string): string | null {\n const value = new URLSearchParams(search).get(PARENT_ORIGIN_QUERY_PARAM);\n return value && value.length > 0 ? value : null;\n}\n\nfunction isParamsMessage(data: unknown, appId: string): data is SkaileAppParamsMessage {\n if (!data || typeof data !== \"object\") return false;\n const m = data as Partial<SkaileAppParamsMessage>;\n // Trim before the emptiness check: a whitespace-only token would survive to\n // the gateway and be refused there. Failing here is the clearer error.\n return (\n m.type === \"skaile_app_params\" &&\n m.appId === appId &&\n typeof m.url === \"string\" &&\n m.url.trim().length > 0 &&\n typeof m.token === \"string\" &&\n m.token.trim().length > 0\n );\n}\n\n/**\n * Ask the embedding host for this app's connection params.\n *\n * Posts a {@link SkaileAppReadyMessage} to the parent and resolves with the\n * matching {@link SkaileAppParamsMessage}. Messages from any other origin, from\n * any window other than the parent, or for another `appId` are ignored — not\n * rejected — because a page may legitimately receive unrelated `postMessage`\n * traffic.\n *\n * Call it again to re-request a fresh token: the token is single-use, so a\n * reconnecting transport needs a new one (see `createSkaileApp`'s `reconnect`).\n *\n * @throws when not embedded, when the parent origin cannot be determined or is\n * unpinnable, or when the host does not reply within `timeoutMs`.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport function resolveAppConnectionParams(\n options: ResolveAppConnectionParamsOptions,\n): Promise<AppConnectionParams> {\n const { appId, timeoutMs = DEFAULT_PARAMS_TIMEOUT_MS } = options;\n const win =\n options.windowRef ?? (globalThis as unknown as { window?: AppWindowLike }).window ?? null;\n if (!win) {\n return Promise.reject(\n new Error(\"resolveAppConnectionParams requires a window (pass `windowRef` off-browser)\"),\n );\n }\n\n const parent = win.parent;\n // A top-level page's `parent` is itself. An app that is not embedded has no\n // host to ask, and posting to itself would hang until the timeout.\n if (!parent || (parent as unknown) === (win as unknown)) {\n return Promise.reject(\n new Error(\"app is not embedded — no parent frame to request params from\"),\n );\n }\n\n const parentOrigin = options.parentOrigin ?? readParentOriginFromQuery(win.location.search);\n if (!parentOrigin) {\n return Promise.reject(\n new Error(\n `cannot determine the host origin: pass \\`parentOrigin\\` or set the \\`${PARENT_ORIGIN_QUERY_PARAM}\\` query param`,\n ),\n );\n }\n // `*` would broadcast the ready ping to whatever is above us, and `null` (an\n // opaque, sandboxed origin) cannot be pinned at all. Both make the peer check\n // meaningless, and the token would then be posted to an unverified window.\n if (parentOrigin === \"*\" || parentOrigin === \"null\") {\n return Promise.reject(new Error(`refusing to use an unpinnable host origin: ${parentOrigin}`));\n }\n\n return new Promise<AppConnectionParams>((resolve, reject) => {\n let settled = false;\n\n const cleanup = () => {\n clearTimeout(timer);\n win.removeEventListener(\"message\", onMessage);\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Error(`timed out after ${timeoutMs}ms waiting for connection params from the host`),\n );\n }, timeoutMs);\n\n function onMessage(event: MessageEventLike): void {\n if (settled) return;\n // Pin the peer on both axes. Origin alone is not enough: any frame served\n // from the host's origin could otherwise answer.\n if (event.origin !== parentOrigin || event.source !== parent) return;\n if (!isParamsMessage(event.data, appId)) return;\n settled = true;\n cleanup();\n resolve({ url: event.data.url, appId, token: event.data.token });\n }\n\n win.addEventListener(\"message\", onMessage);\n\n const ready: SkaileAppReadyMessage = { type: \"skaile_app_ready\", appId };\n try {\n parent.postMessage(ready, parentOrigin);\n } catch (err) {\n settled = true;\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n}\n","/**\n * `defineAppCapability` — author-friendly factory for an embedded app's\n * agent-controllable capabilities.\n *\n * It mirrors the runner's `defineCapability` (`@skaile/workspaces/runner`) but\n * is scoped to the app side: the handler always runs *in the app*, so `side` is\n * fixed to `'app'` and the author never passes it. The originating `appId` is\n * known to the SDK, not the individual capability, so `origin` is stamped by\n * {@link createSkaileApp} at registration time — not here.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { CapabilityAudience, CapabilityOrigin, RenderSpec } from \"@skaile/workspaces/types\";\nimport * as z from \"zod\";\nimport type { AppLogger } from \"./logger.js\";\n\n/**\n * Per-invocation context passed to an app capability handler. Analogous to the\n * runner's `HandlerContext`, but carries the `appId` and the SDK's\n * {@link AppLogger} (there is no server-side `capability:<origin>:<name>` log\n * slice inside the app process).\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type AppHandlerContext = {\n /** The session this app connection is bound to (from the hello ack). */\n sessionId: string;\n /** The embedded app's id. */\n appId: string;\n /** Logger to emit through; defaults to the SDK's configured logger. */\n log: AppLogger;\n};\n\n/**\n * Author-facing arguments to {@link defineAppCapability}. Mirrors the wire\n * {@link import(\"@skaile/workspaces/types\").Capability} fields an app may set,\n * minus `side` / `origin` (fixed / stamped by the SDK) and the runtime-only\n * fields that only make sense platform-side.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefineAppCapabilityArgs<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n> = {\n /**\n * Stable, session-unique name. Convention: `app.<sector>.<verb>` — e.g.\n * `app.ui.switch_contract` (UI sector) or `app.backend.insert_contract`\n * (backend sector). v1 distinguishes sectors by this naming convention.\n */\n name: string;\n /** Human-readable label for user-facing surfaces; falls back to `name`. */\n displayName?: string;\n /** Human/LLM-readable summary; shown to the agent at tool registration. */\n description: string;\n /** Zod schema validating the invocation arguments. Converted to JSON Schema. */\n input: T;\n /** Optional Zod schema for the return value. */\n output?: R;\n /** Lifetime: `session` survives the whole session, `turn` is dropped at end-of-turn. */\n scope?: \"session\" | \"turn\";\n /**\n * When true, the platform shows a confirm UI and waits for user approval\n * before the invocation is forwarded to the app. Mark every mutation\n * (backend writes, destructive UI actions) with this.\n */\n requiresApproval?: boolean;\n /** Documentation hint: side-effect, render, or pure query. */\n kind?: \"effect\" | \"render\" | \"query\";\n /**\n * Audience scoping. Omit for the default (`['llm','user']`). App capabilities\n * default to LLM-only in the command palette (`isUserInvokable`); set\n * `['llm','user']` on the user-facing actions you want to appear in cmdK.\n */\n audience?: CapabilityAudience[];\n /** Appended to the system prompt's `<CAPABILITIES>` section. */\n promptFragment?: string;\n /** Per-capability override for the platform's result round-trip timeout. */\n callTimeoutMs?: number;\n /** Render contract for capabilities that produce a UI surface. */\n render?: RenderSpec;\n /** The handler — runs in the app. Receives validated input + {@link AppHandlerContext}. */\n handler: (\n args: z.infer<T>,\n ctx: AppHandlerContext,\n ) => Promise<R extends z.ZodTypeAny ? z.infer<R> : unknown>;\n};\n\n/**\n * A defined app capability: the wire-format fields the SDK registers (minus the\n * SDK-stamped `origin`), plus the Zod schemas and type-erased handler used for\n * runtime input validation and inbound dispatch.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefinedAppCapability = {\n name: string;\n displayName?: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n /** Always `'app'` — the handler runs in the embedded app. */\n side: \"app\";\n scope?: \"session\" | \"turn\";\n requiresApproval?: boolean;\n kind?: \"effect\" | \"render\" | \"query\";\n audience?: CapabilityAudience[];\n promptFragment?: string;\n callTimeoutMs?: number;\n render?: RenderSpec;\n /** Original Zod schema for runtime input validation. Not serialized to the wire. */\n readonly inputZod: z.ZodTypeAny;\n /** Original Zod schema for the optional output, if declared. Not serialized. */\n readonly outputZod?: z.ZodTypeAny;\n /** Type-erased handler dispatched on an inbound `capability_invoked`. */\n handler: (args: unknown, ctx: AppHandlerContext) => Promise<unknown>;\n};\n\n/** Origin kind an app capability always carries once stamped by the SDK. */\nexport type AppCapabilityOrigin = Extract<CapabilityOrigin, { kind: \"app\" }>;\n\n/**\n * Build a {@link DefinedAppCapability} from author-friendly Zod-typed args.\n * `side` is fixed to `'app'`; `origin` is added by the SDK at registration\n * time (it needs the SDK's `appId`).\n *\n * @example\n * ```ts\n * const switchContract = defineAppCapability({\n * name: 'app.ui.switch_contract',\n * description: 'Switch the visible contract in the app UI.',\n * input: z.object({ contractId: z.string() }),\n * handler: async ({ contractId }, ctx) => {\n * ctx.log.info('switching contract', { contractId });\n * ui.select(contractId);\n * },\n * });\n * ```\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function defineAppCapability<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n>(args: DefineAppCapabilityArgs<T, R>): DefinedAppCapability {\n const inputSchema = z.toJSONSchema(args.input, {\n target: \"draft-07\",\n io: \"input\",\n }) as Record<string, unknown>;\n const outputSchema = args.output\n ? (z.toJSONSchema(args.output, {\n target: \"draft-07\",\n io: \"output\",\n }) as Record<string, unknown>)\n : undefined;\n\n return {\n name: args.name,\n displayName: args.displayName,\n description: args.description,\n inputSchema,\n outputSchema,\n side: \"app\",\n scope: args.scope,\n requiresApproval: args.requiresApproval,\n kind: args.kind,\n audience: args.audience,\n promptFragment: args.promptFragment,\n callTimeoutMs: args.callTimeoutMs,\n render: args.render,\n inputZod: args.input,\n outputZod: args.output,\n handler: args.handler as (args: unknown, ctx: AppHandlerContext) => Promise<unknown>,\n };\n}\n","/**\n * WebSocket-backed {@link AppTransport}.\n *\n * Browser-safe: uses the standard `WebSocket` global (no `ws` dependency, no\n * `node:*` imports), so it runs unchanged in an embedded app's iframe and in a\n * lightweight Node 22+ service. The injected session identity is presented as a\n * bearer **subprotocol** — the same `skaile-bearer.<base64url>` scheme the\n * platform gateway already decodes for runner connections — so the gateway\n * verifies identity at the WS upgrade, before any `app_hello` is processed.\n *\n * Frames are bare JSON of the app-channel messages (no `MessageV2` envelope),\n * matching the runner peer's on-the-wire convention.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { AppClientMessage, AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Matches `BEARER_SUBPROTOCOL_PREFIX` in `@skaile/workspaces/transport`'s ws/auth. */\nconst BEARER_SUBPROTOCOL_PREFIX = \"skaile-bearer.\";\n\n/** Encode a bearer token as a WS subprotocol token (base64url, no padding). */\nfunction encodeBearerSubprotocol(token: string): string {\n // Encode via UTF-8 bytes so a non-ASCII token doesn't make `btoa` throw\n // (btoa is Latin-1 only). The gateway decodes the mirror of this.\n const binary = String.fromCharCode(...new TextEncoder().encode(token));\n const b64 = btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n return BEARER_SUBPROTOCOL_PREFIX + b64;\n}\n\n/** Minimal structural view of the parts of the `WebSocket` API the transport uses. */\ntype WebSocketLike = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", handler: () => void): void;\n addEventListener(type: \"close\", handler: () => void): void;\n addEventListener(type: \"error\", handler: () => void): void;\n addEventListener(type: \"message\", handler: (ev: { data: unknown }) => void): void;\n};\n\n/** Constructor shape for a `WebSocket` implementation (global or injected). */\nexport type WebSocketFactory = new (url: string, protocols?: string | string[]) => WebSocketLike;\n\n/**\n * Supplies a session identity token for one connection attempt.\n *\n * The gateway's token is **single-use** — it is consumed at `app_hello` — so a\n * reconnecting transport cannot replay the one it opened with. Pass a provider\n * (typically a call to `resolveAppConnectionParams`) and every `connect()` mints\n * a fresh token.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport type SessionTokenProvider = () => string | Promise<string>;\n\n/**\n * Options for {@link createWebSocketAppTransport}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface WebSocketAppTransportOptions {\n /** The gateway app-channel URL (e.g. `wss://platform/app-connect`). */\n url: string;\n /**\n * The injected session identity token, or a {@link SessionTokenProvider} that\n * yields a fresh one per connection attempt. Presented as the WS bearer\n * subprotocol; the gateway verifies it and resolves the session/user.\n *\n * A bare string cannot survive a reconnect (the gateway consumes the token at\n * `app_hello`) — pass a provider whenever `reconnect` is enabled on the peer.\n */\n sessionToken: string | SessionTokenProvider;\n /**\n * WebSocket implementation. Defaults to the `WebSocket` global (browser /\n * Node 22+). Injectable for tests or non-standard runtimes.\n */\n WebSocketImpl?: WebSocketFactory;\n /** Connect timeout in ms. Default 10000. */\n connectTimeoutMs?: number;\n}\n\nconst OPEN = 1;\n\n/**\n * Create a WebSocket-backed {@link AppTransport}. The session token is sent as a\n * bearer subprotocol at connect time; app-channel messages are JSON-framed.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createWebSocketAppTransport(options: WebSocketAppTransportOptions): AppTransport {\n const { url, sessionToken, connectTimeoutMs = 10_000 } = options;\n const resolved =\n options.WebSocketImpl ?? (globalThis as { WebSocket?: WebSocketFactory }).WebSocket;\n if (!resolved) {\n throw new Error(\n \"No WebSocket implementation available. Pass `WebSocketImpl` in a runtime without a global WebSocket.\",\n );\n }\n const WebSocketImpl: WebSocketFactory = resolved;\n\n let ws: WebSocketLike | null = null;\n const messageHandlers = new Set<(m: AppServerMessage) => void>();\n const disconnectHandlers = new Set<() => void>();\n\n function connect(): Promise<void> {\n // A provider is resolved per attempt — the gateway consumes the token at\n // `app_hello`, so a reconnect must present a new one. A bare string opens the\n // socket synchronously, so `connect()` still constructs it before it yields.\n if (typeof sessionToken === \"function\") {\n return Promise.resolve(sessionToken()).then(openSocket);\n }\n return openSocket(sessionToken);\n }\n\n function openSocket(token: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(token)]);\n ws = socket;\n\n const timer = setTimeout(() => {\n if (opened) return;\n socket.close(4008, \"connect timeout\");\n reject(new Error(`app transport connect timed out after ${connectTimeoutMs}ms`));\n }, connectTimeoutMs);\n\n socket.addEventListener(\"open\", () => {\n opened = true;\n clearTimeout(timer);\n resolve();\n });\n socket.addEventListener(\"error\", () => {\n if (!opened) {\n clearTimeout(timer);\n reject(new Error(\"app transport connection error\"));\n }\n });\n socket.addEventListener(\"close\", () => {\n clearTimeout(timer);\n if (ws === socket) ws = null;\n for (const h of disconnectHandlers) h();\n });\n socket.addEventListener(\"message\", (ev) => {\n let parsed: AppServerMessage;\n try {\n parsed = JSON.parse(String(ev.data)) as AppServerMessage;\n } catch {\n return;\n }\n for (const h of messageHandlers) h(parsed);\n });\n });\n }\n\n return {\n connect,\n async disconnect() {\n const socket = ws;\n ws = null;\n socket?.close(1000, \"client closed\");\n },\n send(message: AppClientMessage) {\n if (!ws || ws.readyState !== OPEN) {\n throw new Error(\"app transport is not connected\");\n }\n ws.send(JSON.stringify(message));\n },\n onMessage(handler) {\n messageHandlers.add(handler);\n return () => messageHandlers.delete(handler);\n },\n onDisconnect(handler) {\n disconnectHandlers.add(handler);\n return () => disconnectHandlers.delete(handler);\n },\n get connected() {\n return ws?.readyState === OPEN;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../app-sdk/src/logger.ts","../../app-sdk/src/app.ts","../../app-sdk/src/connection-params.ts","../../app-sdk/src/define-app-capability.ts","../../app-sdk/src/ws-transport.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Minimal logger for the app SDK.\n *\n * The SDK is meant to run inside an embedded app (browser or a lightweight\n * Node service), so it deliberately does not pull in the framework's\n * `LogStore` / SQLite logging stack. Authors can pass their own {@link AppLogger}\n * (e.g. a bridge to their app's telemetry) or accept the console-backed default.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\n/** Structured leveled logger the SDK and capability handlers emit through. */\nexport interface AppLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n}\n\n/** A logger that discards everything. The default when none is supplied. */\nexport const noopLogger: AppLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n};\n\n/**\n * Console-backed logger, prefixing every line with `[skaile-app]`. Opt in by\n * passing `logger: consoleLogger` to {@link createSkaileApp} during development.\n */\nexport const consoleLogger: AppLogger = {\n debug(message, data) {\n console.debug(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n info(message, data) {\n console.info(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n warn(message, data) {\n console.warn(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n error(message, data) {\n console.error(`[skaile-app] ${message}`, ...(data !== undefined ? [data] : []));\n },\n};\n","/**\n * `createSkaileApp` — the thin app SDK that makes an embedded app\n * agent-controllable.\n *\n * An app becomes a *capability peer*, mirroring how the runner behaves toward\n * the platform:\n *\n * 1. Opens an authenticated back-connection (via an {@link AppTransport}).\n * 2. Performs the app handshake — sends `app_hello`, awaits `app_hello_ack`\n * (binding the connection to `session + appId`) or `app_hello_nack` (refusal).\n * 3. Registers its `app.*` capabilities via `capability_register`, stamping\n * each with `origin:{kind:'app', appId}` and `side:'app'`. Register /\n * deregister mid-session is supported.\n * 4. Answers inbound `capability_invoked` events by running the local handler\n * and replying with `capability_result` (a thrown handler rides back as\n * `{ error }`).\n *\n * The approval round-trip for `requiresApproval` capabilities is handled\n * entirely platform-side: the gateway parks the invocation for user approval\n * and only forwards `capability_invoked` to the app *after* approval, so the\n * app just marks the capability and answers when asked.\n *\n * The peer reconnects and replays its registrations on a dropped socket, and on\n * a *retryable* refusal — the app boots independently of the session's agent\n * container and can win that race. See {@link ReconnectOptions}.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { Capability } from \"@skaile/workspaces/types\";\nimport { PROTOCOL_VERSION_STRING } from \"@skaile/workspaces/types\";\nimport type { DefinedAppCapability } from \"./define-app-capability.js\";\nimport { type AppLogger, noopLogger } from \"./logger.js\";\nimport type { AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Default timeout waiting for the gateway's `app_hello_ack`. */\nexport const DEFAULT_HELLO_TIMEOUT_MS = 10_000;\n\n/** Defaults mirroring the agent `WebSocketClientTransport`'s backoff. */\nconst DEFAULT_RECONNECT: Required<ReconnectOptions> = {\n maxAttempts: 5,\n baseMs: 1_000,\n maxMs: 30_000,\n};\n\n/**\n * Reconnect / retry policy. Set `reconnect: false` on {@link SkaileAppOptions}\n * to disable entirely (one attempt, no replay).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ReconnectOptions {\n /** Attempts before giving up. Default 5. */\n maxAttempts?: number;\n /** First backoff delay in ms; doubles each attempt. Default 1000. */\n baseMs?: number;\n /** Backoff ceiling in ms. Default 30000. */\n maxMs?: number;\n}\n\n/**\n * The gateway refused the handshake. `retryable` mirrors the wire field: `true`\n * for a transient refusal (the agent container is not up yet), `false` for a\n * permanent one (bad token, `appId` not permitted).\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport class AppHandshakeRefusedError extends Error {\n readonly retryable: boolean;\n constructor(reason: string, retryable: boolean) {\n super(`app handshake refused: ${reason}`);\n this.name = \"AppHandshakeRefusedError\";\n this.retryable = retryable;\n }\n}\n\n/**\n * Options for {@link createSkaileApp}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileAppOptions {\n /** The embedded app's stable id. Every registered capability is bound to it. */\n appId: string;\n /**\n * The app back-connection. Inject {@link createWebSocketAppTransport} in\n * production or a fake in tests. The transport carries the injected session\n * identity as its connection credential.\n *\n * With `reconnect` enabled the transport must be able to produce a **fresh**\n * token per attempt — the gateway consumes it at `app_hello`. Pass a\n * `SessionTokenProvider` rather than a bare string.\n */\n transport: AppTransport;\n /** Protocol version the SDK announces in `app_hello`. Defaults to the SDK's. */\n protocolVersion?: string;\n /** Timeout waiting for `app_hello_ack`. Defaults to {@link DEFAULT_HELLO_TIMEOUT_MS}. */\n helloTimeoutMs?: number;\n /** Logger for the SDK and (by default) capability handlers. Defaults to {@link noopLogger}. */\n logger?: AppLogger;\n /**\n * Reconnect policy, or `false` to disable. Defaults to\n * `{maxAttempts:5, baseMs:1000, maxMs:30000}`.\n *\n * @since 3.5.0\n */\n reconnect?: ReconnectOptions | false;\n}\n\n/** Result of a successful handshake — the bound session context. */\nexport interface AppSession {\n sessionId: string;\n userId?: string;\n}\n\n/**\n * A connected (or connectable) Skaile app. Register capabilities before or\n * after {@link connect}; post-connect changes are pushed to the gateway\n * immediately.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface SkaileApp {\n /**\n * Add (or replace, by name) an app capability. Registering the same name\n * again replaces it — mirrors the registry's replace-by-name semantics. When\n * already connected, a `capability_register` is sent immediately.\n */\n registerCapability(cap: DefinedAppCapability): void;\n /**\n * Remove a capability by name. No-op when unknown. When connected, a\n * `capability_deregister` is sent immediately so the tool disappears from the\n * agent's catalog.\n */\n deregisterCapability(name: string): void;\n /** Wire-format snapshot of the currently registered capabilities (origin stamped). */\n readonly capabilities: readonly Capability[];\n /** The bound session once connected; `null` before the handshake and while reconnecting. */\n readonly session: AppSession | null;\n /**\n * Open the connection, perform the handshake, and register the current\n * capabilities. Resolves with the bound {@link AppSession}. Rejects if the\n * gateway refuses permanently, the handshake times out, or the transport\n * disconnects mid-handshake. A *retryable* refusal is retried per the\n * reconnect policy before rejecting.\n */\n connect(): Promise<AppSession>;\n /**\n * Subscribe to session binding changes. Fires on **every** bind — including\n * the initial `connect()` — with the new {@link AppSession}, and with `null`\n * whenever the connection drops. Returns an unsubscribe function.\n *\n * @since 3.5.0\n */\n onSessionChange(handler: (session: AppSession | null) => void): () => void;\n /** Disconnect and tear down. Idempotent. Cancels any pending reconnect. */\n close(): Promise<void>;\n}\n\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * Create a {@link SkaileApp}. See the module doc for the peer lifecycle.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createSkaileApp(options: SkaileAppOptions): SkaileApp {\n const {\n appId,\n transport,\n protocolVersion = PROTOCOL_VERSION_STRING,\n helloTimeoutMs = DEFAULT_HELLO_TIMEOUT_MS,\n logger = noopLogger,\n } = options;\n\n const reconnectEnabled = options.reconnect !== false;\n const policy: Required<ReconnectOptions> = {\n ...DEFAULT_RECONNECT,\n ...(options.reconnect === false ? {} : options.reconnect),\n };\n\n const registry = new Map<string, DefinedAppCapability>();\n const sessionListeners = new Set<(s: AppSession | null) => void>();\n let session: AppSession | null = null;\n let closed = false;\n let connecting = false;\n let subscribed = false;\n // Bumped by close(). A reconnect loop parked on its backoff timer captures the\n // epoch it started in and abandons if close() (or a close/connect cycle) has\n // since happened — otherwise it would resurrect a torn-down app.\n let epoch = 0;\n let unsubscribeMessage: (() => void) | null = null;\n let unsubscribeDisconnect: (() => void) | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n /** Resolver of the backoff sleep, so close() can wake a parked loop. */\n let reconnectWake: (() => void) | null = null;\n\n /** Non-null only while a handshake is in flight. */\n let pending: { settle: (err: Error | null, s?: AppSession) => void } | null = null;\n\n function toWire(cap: DefinedAppCapability): Capability {\n return {\n name: cap.name,\n displayName: cap.displayName,\n description: cap.description,\n inputSchema: cap.inputSchema,\n outputSchema: cap.outputSchema,\n side: \"app\",\n origin: { kind: \"app\", appId },\n scope: cap.scope,\n requiresApproval: cap.requiresApproval,\n kind: cap.kind,\n audience: cap.audience,\n promptFragment: cap.promptFragment,\n callTimeoutMs: cap.callTimeoutMs,\n render: cap.render,\n };\n }\n\n function setSession(next: AppSession | null): void {\n session = next;\n for (const h of sessionListeners) h(next);\n }\n\n function registerCapability(cap: DefinedAppCapability): void {\n registry.set(cap.name, cap);\n if (session) {\n transport.send({ type: \"capability_register\", capabilities: [toWire(cap)] });\n logger.debug(\"registered capability (live)\", { name: cap.name });\n }\n }\n\n function deregisterCapability(name: string): void {\n if (!registry.delete(name)) return;\n if (session) {\n transport.send({ type: \"capability_deregister\", names: [name] });\n logger.debug(\"deregistered capability (live)\", { name });\n }\n }\n\n /** Dispatch an inbound invocation to the local handler; reply with the result. */\n async function handleInvocation(callId: string, name: string, input: unknown): Promise<void> {\n const cap = registry.get(name);\n if (!cap) {\n transport.send({\n type: \"capability_result\",\n callId,\n result: { error: `Unknown app capability: ${name}` },\n });\n return;\n }\n try {\n const validated = cap.inputZod.parse(input);\n const ctx = { sessionId: session?.sessionId ?? \"\", appId, log: logger };\n const result = await cap.handler(validated, ctx);\n transport.send({ type: \"capability_result\", callId, result });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\"capability handler failed\", { name, error: message });\n transport.send({ type: \"capability_result\", callId, result: { error: message } });\n }\n }\n\n function onMessage(message: AppServerMessage): void {\n switch (message.type) {\n case \"app_hello_ack\": {\n const bound: AppSession = { sessionId: message.sessionId, userId: message.userId };\n pending?.settle(null, bound);\n return;\n }\n case \"app_hello_nack\":\n pending?.settle(new AppHandshakeRefusedError(message.error, message.retryable === true));\n return;\n case \"capability_invoked\":\n void handleInvocation(message.callId, message.name, message.input);\n return;\n }\n }\n\n function onDisconnect(): void {\n if (pending) {\n pending.settle(new Error(\"transport disconnected during handshake\"));\n return;\n }\n if (closed || !session) return;\n logger.warn(\"app connection lost\", { appId });\n setSession(null);\n if (!reconnectEnabled) return;\n // The loop holds the same guard connect() uses: a manual connect() while it\n // runs would start a second handshake, and both would overwrite `pending`.\n connecting = true;\n const loopEpoch = epoch;\n void reconnectLoop().finally(() => {\n // A close()/connect() cycle since this loop started owns the guard now.\n if (epoch === loopEpoch) connecting = false;\n });\n }\n\n function ensureSubscribed(): void {\n if (subscribed) return;\n unsubscribeMessage = transport.onMessage(onMessage);\n unsubscribeDisconnect = transport.onDisconnect(onDisconnect);\n subscribed = true;\n }\n\n /** One attempt: open the socket, exchange hello/ack, replay the registry. */\n function performHandshake(): Promise<AppSession> {\n ensureSubscribed();\n return transport.connect().then(\n () =>\n new Promise<AppSession>((resolve, reject) => {\n if (closed) {\n reject(new Error(\"app closed during handshake\"));\n return;\n }\n let settled = false;\n const timer = setTimeout(() => {\n pending?.settle(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));\n }, helloTimeoutMs);\n\n pending = {\n settle: (err, bound) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n pending = null;\n if (err || !bound) {\n reject(err ?? new Error(\"app handshake failed\"));\n return;\n }\n setSession(bound);\n logger.info(\"app connection established\", { appId, sessionId: bound.sessionId });\n // Replay the full surface — on a reconnect the gateway dropped our\n // caps when the previous connection closed.\n const wire = Array.from(registry.values()).map(toWire);\n if (wire.length > 0) {\n transport.send({ type: \"capability_register\", capabilities: wire });\n }\n resolve(bound);\n },\n };\n\n transport.send({ type: \"app_hello\", appId, protocolVersion });\n }),\n (err) => Promise.reject(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n const backoffMs = (attempt: number) =>\n Math.min(policy.maxMs, policy.baseMs * 2 ** Math.max(0, attempt - 1));\n\n /** Rebind after a dropped socket. Stops on a permanent refusal or close(). */\n async function reconnectLoop(): Promise<void> {\n const myEpoch = epoch;\n for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {\n const wait = backoffMs(attempt);\n logger.debug(\"scheduling app reconnect\", { appId, attempt, waitMs: wait });\n // close() cancels the timer, so it also has to hand us the resolver —\n // otherwise this await never settles and the loop frame parks forever,\n // pinning the transport and every other captured binding.\n await new Promise<void>((r) => {\n reconnectWake = r;\n reconnectTimer = setTimeout(r, wait);\n });\n reconnectWake = null;\n if (closed || epoch !== myEpoch) {\n logger.debug(\"app reconnect cancelled\", { appId, attempt });\n return;\n }\n try {\n await performHandshake();\n logger.info(\"app connection re-established\", { appId });\n return;\n } catch (err) {\n if (err instanceof AppHandshakeRefusedError && !err.retryable) {\n logger.error(\"app reconnect refused permanently\", { appId, error: err.message });\n return;\n }\n logger.warn(\"app reconnect attempt failed\", {\n appId,\n attempt,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n logger.error(\"app reconnect gave up\", { appId, attempts: policy.maxAttempts });\n }\n\n async function connect(): Promise<AppSession> {\n if (session) return session;\n if (connecting) throw new Error(\"app connect() is already in progress\");\n connecting = true;\n // A closed app is reusable: connect() reopens it. Any reconnect loop still\n // parked from the previous life is fenced off by the epoch close() bumped.\n closed = false;\n try {\n for (let attempt = 1; ; attempt++) {\n try {\n return await performHandshake();\n } catch (err) {\n // Only a *retryable* refusal is worth another attempt on the initial\n // connect: the app raced the session's agent container on boot. Every\n // other failure (bad token, timeout, transport error) is terminal here.\n const transient = err instanceof AppHandshakeRefusedError && err.retryable;\n if (!transient || !reconnectEnabled || attempt >= policy.maxAttempts || closed) {\n throw err;\n }\n logger.debug(\"app handshake refused (retryable), retrying\", { appId, attempt });\n await delay(backoffMs(attempt));\n }\n }\n } finally {\n connecting = false;\n }\n }\n\n async function close(): Promise<void> {\n closed = true;\n epoch++;\n clearTimeout(reconnectTimer);\n // Wake a loop parked on its backoff so it observes `closed` and returns,\n // rather than awaiting a timer that will now never fire.\n reconnectWake?.();\n reconnectWake = null;\n connecting = false;\n // Abort an in-flight handshake — rejects the pending connect and clears its\n // timer so nothing leaks past close().\n pending?.settle(new Error(\"app closed during handshake\"));\n unsubscribeMessage?.();\n unsubscribeDisconnect?.();\n unsubscribeMessage = null;\n unsubscribeDisconnect = null;\n subscribed = false;\n session = null;\n await transport.disconnect();\n }\n\n return {\n registerCapability,\n deregisterCapability,\n get capabilities() {\n return Array.from(registry.values()).map(toWire);\n },\n get session() {\n return session;\n },\n connect,\n onSessionChange(handler) {\n sessionListeners.add(handler);\n return () => sessionListeners.delete(handler);\n },\n close,\n };\n}\n","/**\n * Connection-param handshake between the embedding host and the app frame.\n *\n * An embedded app needs three things to open its back-connection: the gateway\n * URL, its own `appId`, and a session identity token. The token is a **secret**,\n * so it must not ride in the iframe `src` — the preview surface that serves the\n * app is unauthenticated, and a URL lands in access logs, `Referer` headers, and\n * browser history. Instead the host posts it over `postMessage`, and the app\n * asks for it with a ready ping once its script is running.\n *\n * The host cannot simply post on iframe `load`: it has no way to know when the\n * app's script is listening. So the app speaks first.\n *\n * ```text\n * app → host { type: 'skaile_app_ready', appId } targetOrigin = parentOrigin\n * host → app { type: 'skaile_app_params', appId, url, token } targetOrigin = appOrigin\n * ```\n *\n * Both sides pin the peer: the app checks `event.origin === parentOrigin` and\n * that the message came from its own parent; the host checks the origin and that\n * `event.source` is the very iframe it rendered. The app learns the expected\n * parent origin from a **non-secret** query param on its `src`\n * ({@link PARENT_ORIGIN_QUERY_PARAM}), which is safe to leak precisely because it\n * is not a credential.\n *\n * @category App SDK\n * @since 3.5.0\n */\n\n/**\n * Query param on the app's iframe `src` carrying the embedder's origin. Not a\n * secret — it is the value the app pins its `postMessage` peer to.\n */\nexport const PARENT_ORIGIN_QUERY_PARAM = \"skaileParentOrigin\";\n\n/** Default timeout waiting for the host's params reply. */\nexport const DEFAULT_PARAMS_TIMEOUT_MS = 10_000;\n\n/** First gap between ready pings. Grows by {@link PING_BACKOFF_FACTOR} up to {@link MAX_PING_INTERVAL_MS}. */\nexport const INITIAL_PING_INTERVAL_MS = 100;\n\n/**\n * Ceiling for the ready-ping gap. The host mints a fresh single-use ticket for\n * *every* ping it answers, so pinging is not free — back off rather than hammer.\n */\nexport const MAX_PING_INTERVAL_MS = 1_000;\n\nconst PING_BACKOFF_FACTOR = 2;\n\n/** App → host. Announces the app frame is listening and wants its params. */\nexport type SkaileAppReadyMessage = {\n type: \"skaile_app_ready\";\n /** The app asking. The host answers only for an app it actually embedded. */\n appId: string;\n};\n\n/** Host → app. Carries the gateway URL and the single-use session identity token. */\nexport type SkaileAppParamsMessage = {\n type: \"skaile_app_params\";\n /** Echoed back so a frame embedding several apps can disambiguate. */\n appId: string;\n /** The gateway app-channel URL, e.g. `wss://platform/app-connect`. */\n url: string;\n /** Single-use session identity token. Consumed by the gateway at `app_hello`. */\n token: string;\n};\n\n/** Resolved params, ready to hand to `createWebSocketAppTransport` + `createSkaileApp`. */\nexport type AppConnectionParams = {\n url: string;\n appId: string;\n token: string;\n};\n\n/** The subset of `postMessage` targets this module needs. */\nexport type PostMessageTarget = {\n postMessage(message: unknown, targetOrigin: string): void;\n};\n\n/** Structural view of a `MessageEvent`, so tests need no DOM. */\nexport type MessageEventLike = {\n readonly origin: string;\n readonly source: unknown;\n readonly data: unknown;\n};\n\n/** Structural view of the `window` bits this module needs. Injectable for tests. */\nexport type AppWindowLike = {\n readonly parent: PostMessageTarget | null;\n readonly location: { readonly search: string };\n addEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n removeEventListener(type: \"message\", handler: (event: MessageEventLike) => void): void;\n};\n\n/**\n * Options for {@link resolveAppConnectionParams}.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport interface ResolveAppConnectionParamsOptions {\n /** The embedded app's stable id. Must match the params the host replies with. */\n appId: string;\n /**\n * Expected origin of the embedding host. Defaults to the\n * {@link PARENT_ORIGIN_QUERY_PARAM} query param on the current URL. Never `\"*\"`\n * or `\"null\"` — an unpinned peer defeats the point of not putting the token in\n * the URL.\n */\n parentOrigin?: string;\n /** How long to wait for the host's reply. Defaults to {@link DEFAULT_PARAMS_TIMEOUT_MS}. */\n timeoutMs?: number;\n /** Window to listen and post on. Defaults to the global. Injectable for tests. */\n windowRef?: AppWindowLike;\n}\n\nfunction readParentOriginFromQuery(search: string): string | null {\n const value = new URLSearchParams(search).get(PARENT_ORIGIN_QUERY_PARAM);\n return value && value.length > 0 ? value : null;\n}\n\nfunction isParamsMessage(data: unknown, appId: string): data is SkaileAppParamsMessage {\n if (!data || typeof data !== \"object\") return false;\n const m = data as Partial<SkaileAppParamsMessage>;\n // Trim before the emptiness check: a whitespace-only token would survive to\n // the gateway and be refused there. Failing here is the clearer error.\n return (\n m.type === \"skaile_app_params\" &&\n m.appId === appId &&\n typeof m.url === \"string\" &&\n m.url.trim().length > 0 &&\n typeof m.token === \"string\" &&\n m.token.trim().length > 0\n );\n}\n\n/**\n * Ask the embedding host for this app's connection params.\n *\n * Posts a {@link SkaileAppReadyMessage} to the parent and resolves with the\n * matching {@link SkaileAppParamsMessage}. Messages from any other origin, from\n * any window other than the parent, or for another `appId` are ignored — not\n * rejected — because a page may legitimately receive unrelated `postMessage`\n * traffic.\n *\n * Call it again to re-request a fresh token: the token is single-use, so a\n * reconnecting transport needs a new one (see `createSkaileApp`'s `reconnect`).\n *\n * @throws when not embedded, when the parent origin cannot be determined or is\n * unpinnable, or when the host does not reply within `timeoutMs`.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport function resolveAppConnectionParams(\n options: ResolveAppConnectionParamsOptions,\n): Promise<AppConnectionParams> {\n const { appId, timeoutMs = DEFAULT_PARAMS_TIMEOUT_MS } = options;\n const win =\n options.windowRef ?? (globalThis as unknown as { window?: AppWindowLike }).window ?? null;\n if (!win) {\n return Promise.reject(\n new Error(\"resolveAppConnectionParams requires a window (pass `windowRef` off-browser)\"),\n );\n }\n\n const parent = win.parent;\n // A top-level page's `parent` is itself. An app that is not embedded has no\n // host to ask, and posting to itself would hang until the timeout.\n if (!parent || (parent as unknown) === (win as unknown)) {\n return Promise.reject(\n new Error(\"app is not embedded — no parent frame to request params from\"),\n );\n }\n\n const parentOrigin = options.parentOrigin ?? readParentOriginFromQuery(win.location.search);\n if (!parentOrigin) {\n return Promise.reject(\n new Error(\n `cannot determine the host origin: pass \\`parentOrigin\\` or set the \\`${PARENT_ORIGIN_QUERY_PARAM}\\` query param`,\n ),\n );\n }\n // `*` would broadcast the ready ping to whatever is above us, and `null` (an\n // opaque, sandboxed origin) cannot be pinned at all. Both make the peer check\n // meaningless, and the token would then be posted to an unverified window.\n if (parentOrigin === \"*\" || parentOrigin === \"null\") {\n return Promise.reject(new Error(`refusing to use an unpinnable host origin: ${parentOrigin}`));\n }\n\n // Bind the narrowed peer once: the guards above proved both are non-null, but\n // that narrowing does not survive into the hoisted `ping` declaration below.\n const peer: PostMessageTarget = parent;\n const peerOrigin: string = parentOrigin;\n\n return new Promise<AppConnectionParams>((resolve, reject) => {\n let settled = false;\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pingInterval = INITIAL_PING_INTERVAL_MS;\n\n const cleanup = () => {\n clearTimeout(timer);\n clearTimeout(pingTimer);\n win.removeEventListener(\"message\", onMessage);\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(\n new Error(`timed out after ${timeoutMs}ms waiting for connection params from the host`),\n );\n }, timeoutMs);\n\n function onMessage(event: MessageEventLike): void {\n if (settled) return;\n // Pin the peer on both axes. Origin alone is not enough: any frame served\n // from the host's origin could otherwise answer.\n if (event.origin !== peerOrigin || event.source !== peer) return;\n if (!isParamsMessage(event.data, appId)) return;\n settled = true;\n cleanup();\n resolve({ url: event.data.url, appId, token: event.data.token });\n }\n\n win.addEventListener(\"message\", onMessage);\n\n const ready: SkaileAppReadyMessage = { type: \"skaile_app_ready\", appId };\n\n /**\n * Keep asking until the host answers. The host only mints in response to a\n * ping and never speaks unprompted, so a single ping is a race the app loses\n * whenever it boots before the host's own state has settled — the ping is\n * dropped and nothing re-asks. Backs off because each answered ping costs the\n * host a single-use ticket.\n */\n function ping(): void {\n if (settled) return;\n try {\n peer.postMessage(ready, peerOrigin);\n } catch (err) {\n settled = true;\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n pingTimer = setTimeout(ping, pingInterval);\n pingInterval = Math.min(pingInterval * PING_BACKOFF_FACTOR, MAX_PING_INTERVAL_MS);\n }\n\n ping();\n });\n}\n","/**\n * `defineAppCapability` — author-friendly factory for an embedded app's\n * agent-controllable capabilities.\n *\n * It mirrors the runner's `defineCapability` (`@skaile/workspaces/runner`) but\n * is scoped to the app side: the handler always runs *in the app*, so `side` is\n * fixed to `'app'` and the author never passes it. The originating `appId` is\n * known to the SDK, not the individual capability, so `origin` is stamped by\n * {@link createSkaileApp} at registration time — not here.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { CapabilityAudience, CapabilityOrigin, RenderSpec } from \"@skaile/workspaces/types\";\nimport * as z from \"zod\";\nimport type { AppLogger } from \"./logger.js\";\n\n/**\n * Per-invocation context passed to an app capability handler. Analogous to the\n * runner's `HandlerContext`, but carries the `appId` and the SDK's\n * {@link AppLogger} (there is no server-side `capability:<origin>:<name>` log\n * slice inside the app process).\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type AppHandlerContext = {\n /** The session this app connection is bound to (from the hello ack). */\n sessionId: string;\n /** The embedded app's id. */\n appId: string;\n /** Logger to emit through; defaults to the SDK's configured logger. */\n log: AppLogger;\n};\n\n/**\n * Author-facing arguments to {@link defineAppCapability}. Mirrors the wire\n * {@link import(\"@skaile/workspaces/types\").Capability} fields an app may set,\n * minus `side` / `origin` (fixed / stamped by the SDK) and the runtime-only\n * fields that only make sense platform-side.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefineAppCapabilityArgs<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n> = {\n /**\n * Stable, session-unique name. Convention: `app.<sector>.<verb>` — e.g.\n * `app.ui.switch_contract` (UI sector) or `app.backend.insert_contract`\n * (backend sector). v1 distinguishes sectors by this naming convention.\n */\n name: string;\n /** Human-readable label for user-facing surfaces; falls back to `name`. */\n displayName?: string;\n /** Human/LLM-readable summary; shown to the agent at tool registration. */\n description: string;\n /** Zod schema validating the invocation arguments. Converted to JSON Schema. */\n input: T;\n /** Optional Zod schema for the return value. */\n output?: R;\n /** Lifetime: `session` survives the whole session, `turn` is dropped at end-of-turn. */\n scope?: \"session\" | \"turn\";\n /**\n * When true, the platform shows a confirm UI and waits for user approval\n * before the invocation is forwarded to the app. Mark every mutation\n * (backend writes, destructive UI actions) with this.\n */\n requiresApproval?: boolean;\n /** Documentation hint: side-effect, render, or pure query. */\n kind?: \"effect\" | \"render\" | \"query\";\n /**\n * Audience scoping. Omit for the default (`['llm','user']`). App capabilities\n * default to LLM-only in the command palette (`isUserInvokable`); set\n * `['llm','user']` on the user-facing actions you want to appear in cmdK.\n */\n audience?: CapabilityAudience[];\n /** Appended to the system prompt's `<CAPABILITIES>` section. */\n promptFragment?: string;\n /** Per-capability override for the platform's result round-trip timeout. */\n callTimeoutMs?: number;\n /** Render contract for capabilities that produce a UI surface. */\n render?: RenderSpec;\n /** The handler — runs in the app. Receives validated input + {@link AppHandlerContext}. */\n handler: (\n args: z.infer<T>,\n ctx: AppHandlerContext,\n ) => Promise<R extends z.ZodTypeAny ? z.infer<R> : unknown>;\n};\n\n/**\n * A defined app capability: the wire-format fields the SDK registers (minus the\n * SDK-stamped `origin`), plus the Zod schemas and type-erased handler used for\n * runtime input validation and inbound dispatch.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport type DefinedAppCapability = {\n name: string;\n displayName?: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n /** Always `'app'` — the handler runs in the embedded app. */\n side: \"app\";\n scope?: \"session\" | \"turn\";\n requiresApproval?: boolean;\n kind?: \"effect\" | \"render\" | \"query\";\n audience?: CapabilityAudience[];\n promptFragment?: string;\n callTimeoutMs?: number;\n render?: RenderSpec;\n /** Original Zod schema for runtime input validation. Not serialized to the wire. */\n readonly inputZod: z.ZodTypeAny;\n /** Original Zod schema for the optional output, if declared. Not serialized. */\n readonly outputZod?: z.ZodTypeAny;\n /** Type-erased handler dispatched on an inbound `capability_invoked`. */\n handler: (args: unknown, ctx: AppHandlerContext) => Promise<unknown>;\n};\n\n/** Origin kind an app capability always carries once stamped by the SDK. */\nexport type AppCapabilityOrigin = Extract<CapabilityOrigin, { kind: \"app\" }>;\n\n/**\n * Build a {@link DefinedAppCapability} from author-friendly Zod-typed args.\n * `side` is fixed to `'app'`; `origin` is added by the SDK at registration\n * time (it needs the SDK's `appId`).\n *\n * @example\n * ```ts\n * const switchContract = defineAppCapability({\n * name: 'app.ui.switch_contract',\n * description: 'Switch the visible contract in the app UI.',\n * input: z.object({ contractId: z.string() }),\n * handler: async ({ contractId }, ctx) => {\n * ctx.log.info('switching contract', { contractId });\n * ui.select(contractId);\n * },\n * });\n * ```\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function defineAppCapability<\n T extends z.ZodTypeAny,\n R extends z.ZodTypeAny | undefined = undefined,\n>(args: DefineAppCapabilityArgs<T, R>): DefinedAppCapability {\n const inputSchema = z.toJSONSchema(args.input, {\n target: \"draft-07\",\n io: \"input\",\n }) as Record<string, unknown>;\n const outputSchema = args.output\n ? (z.toJSONSchema(args.output, {\n target: \"draft-07\",\n io: \"output\",\n }) as Record<string, unknown>)\n : undefined;\n\n return {\n name: args.name,\n displayName: args.displayName,\n description: args.description,\n inputSchema,\n outputSchema,\n side: \"app\",\n scope: args.scope,\n requiresApproval: args.requiresApproval,\n kind: args.kind,\n audience: args.audience,\n promptFragment: args.promptFragment,\n callTimeoutMs: args.callTimeoutMs,\n render: args.render,\n inputZod: args.input,\n outputZod: args.output,\n handler: args.handler as (args: unknown, ctx: AppHandlerContext) => Promise<unknown>,\n };\n}\n","/**\n * WebSocket-backed {@link AppTransport}.\n *\n * Browser-safe: uses the standard `WebSocket` global (no `ws` dependency, no\n * `node:*` imports), so it runs unchanged in an embedded app's iframe and in a\n * lightweight Node 22+ service. The injected session identity is presented as a\n * bearer **subprotocol** — the same `skaile-bearer.<base64url>` scheme the\n * platform gateway already decodes for runner connections — so the gateway\n * verifies identity at the WS upgrade, before any `app_hello` is processed.\n *\n * Frames are bare JSON of the app-channel messages (no `MessageV2` envelope),\n * matching the runner peer's on-the-wire convention.\n *\n * @category App SDK\n * @since 3.4.0\n */\n\nimport type { AppClientMessage, AppServerMessage, AppTransport } from \"./transport.js\";\n\n/** Matches `BEARER_SUBPROTOCOL_PREFIX` in `@skaile/workspaces/transport`'s ws/auth. */\nconst BEARER_SUBPROTOCOL_PREFIX = \"skaile-bearer.\";\n\n/** Encode a bearer token as a WS subprotocol token (base64url, no padding). */\nfunction encodeBearerSubprotocol(token: string): string {\n // Encode via UTF-8 bytes so a non-ASCII token doesn't make `btoa` throw\n // (btoa is Latin-1 only). The gateway decodes the mirror of this.\n const binary = String.fromCharCode(...new TextEncoder().encode(token));\n const b64 = btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n return BEARER_SUBPROTOCOL_PREFIX + b64;\n}\n\n/** Minimal structural view of the parts of the `WebSocket` API the transport uses. */\ntype WebSocketLike = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", handler: () => void): void;\n addEventListener(type: \"close\", handler: () => void): void;\n addEventListener(type: \"error\", handler: () => void): void;\n addEventListener(type: \"message\", handler: (ev: { data: unknown }) => void): void;\n};\n\n/** Constructor shape for a `WebSocket` implementation (global or injected). */\nexport type WebSocketFactory = new (url: string, protocols?: string | string[]) => WebSocketLike;\n\n/**\n * Supplies a session identity token for one connection attempt.\n *\n * The gateway's token is **single-use** — it is consumed at `app_hello` — so a\n * reconnecting transport cannot replay the one it opened with. Pass a provider\n * (typically a call to `resolveAppConnectionParams`) and every `connect()` mints\n * a fresh token.\n *\n * @category App SDK\n * @since 3.5.0\n */\nexport type SessionTokenProvider = () => string | Promise<string>;\n\n/**\n * Options for {@link createWebSocketAppTransport}.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport interface WebSocketAppTransportOptions {\n /** The gateway app-channel URL (e.g. `wss://platform/app-connect`). */\n url: string;\n /**\n * The injected session identity token, or a {@link SessionTokenProvider} that\n * yields a fresh one per connection attempt. Presented as the WS bearer\n * subprotocol; the gateway verifies it and resolves the session/user.\n *\n * A bare string cannot survive a reconnect (the gateway consumes the token at\n * `app_hello`) — pass a provider whenever `reconnect` is enabled on the peer.\n */\n sessionToken: string | SessionTokenProvider;\n /**\n * WebSocket implementation. Defaults to the `WebSocket` global (browser /\n * Node 22+). Injectable for tests or non-standard runtimes.\n */\n WebSocketImpl?: WebSocketFactory;\n /** Connect timeout in ms. Default 10000. */\n connectTimeoutMs?: number;\n}\n\nconst OPEN = 1;\n\n/**\n * Create a WebSocket-backed {@link AppTransport}. The session token is sent as a\n * bearer subprotocol at connect time; app-channel messages are JSON-framed.\n *\n * @category App SDK\n * @since 3.4.0\n */\nexport function createWebSocketAppTransport(options: WebSocketAppTransportOptions): AppTransport {\n const { url, sessionToken, connectTimeoutMs = 10_000 } = options;\n const resolved =\n options.WebSocketImpl ?? (globalThis as { WebSocket?: WebSocketFactory }).WebSocket;\n if (!resolved) {\n throw new Error(\n \"No WebSocket implementation available. Pass `WebSocketImpl` in a runtime without a global WebSocket.\",\n );\n }\n const WebSocketImpl: WebSocketFactory = resolved;\n\n let ws: WebSocketLike | null = null;\n const messageHandlers = new Set<(m: AppServerMessage) => void>();\n const disconnectHandlers = new Set<() => void>();\n\n function connect(): Promise<void> {\n // A provider is resolved per attempt — the gateway consumes the token at\n // `app_hello`, so a reconnect must present a new one. A bare string opens the\n // socket synchronously, so `connect()` still constructs it before it yields.\n if (typeof sessionToken === \"function\") {\n return Promise.resolve(sessionToken()).then(openSocket);\n }\n return openSocket(sessionToken);\n }\n\n function openSocket(token: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(token)]);\n ws = socket;\n\n const timer = setTimeout(() => {\n if (opened) return;\n socket.close(4008, \"connect timeout\");\n reject(new Error(`app transport connect timed out after ${connectTimeoutMs}ms`));\n }, connectTimeoutMs);\n\n socket.addEventListener(\"open\", () => {\n opened = true;\n clearTimeout(timer);\n resolve();\n });\n socket.addEventListener(\"error\", () => {\n if (!opened) {\n clearTimeout(timer);\n reject(new Error(\"app transport connection error\"));\n }\n });\n socket.addEventListener(\"close\", () => {\n clearTimeout(timer);\n if (ws === socket) ws = null;\n for (const h of disconnectHandlers) h();\n });\n socket.addEventListener(\"message\", (ev) => {\n let parsed: AppServerMessage;\n try {\n parsed = JSON.parse(String(ev.data)) as AppServerMessage;\n } catch {\n return;\n }\n for (const h of messageHandlers) h(parsed);\n });\n });\n }\n\n return {\n connect,\n async disconnect() {\n const socket = ws;\n ws = null;\n socket?.close(1000, \"client closed\");\n },\n send(message: AppClientMessage) {\n if (!ws || ws.readyState !== OPEN) {\n throw new Error(\"app transport is not connected\");\n }\n ws.send(JSON.stringify(message));\n },\n onMessage(handler) {\n messageHandlers.add(handler);\n return () => messageHandlers.delete(handler);\n },\n onDisconnect(handler) {\n disconnectHandlers.add(handler);\n return () => disconnectHandlers.delete(handler);\n },\n get connected() {\n return ws?.readyState === OPEN;\n },\n };\n}\n"]}
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
export declare const PARENT_ORIGIN_QUERY_PARAM = "skaileParentOrigin";
|
|
34
34
|
/** Default timeout waiting for the host's params reply. */
|
|
35
35
|
export declare const DEFAULT_PARAMS_TIMEOUT_MS = 10000;
|
|
36
|
+
/** First gap between ready pings. Grows by {@link PING_BACKOFF_FACTOR} up to {@link MAX_PING_INTERVAL_MS}. */
|
|
37
|
+
export declare const INITIAL_PING_INTERVAL_MS = 100;
|
|
38
|
+
/**
|
|
39
|
+
* Ceiling for the ready-ping gap. The host mints a fresh single-use ticket for
|
|
40
|
+
* *every* ping it answers, so pinging is not free — back off rather than hammer.
|
|
41
|
+
*/
|
|
42
|
+
export declare const MAX_PING_INTERVAL_MS = 1000;
|
|
36
43
|
/** App → host. Announces the app frame is listening and wants its params. */
|
|
37
44
|
export type SkaileAppReadyMessage = {
|
|
38
45
|
type: "skaile_app_ready";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-params.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/connection-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;GAGG;AACH,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,kBAAkB,CAAC;IACzB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qFAAqF;AACrF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;IACpF,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC;IAChD,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAsBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"connection-params.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/connection-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;GAGG;AACH,eAAO,MAAM,yBAAyB,uBAAuB,CAAC;AAE9D,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,8GAA8G;AAC9G,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAI1C,6EAA6E;AAC7E,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,kBAAkB,CAAC;IACzB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qFAAqF;AACrF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;IACpF,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC;IAChD,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAsBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,mBAAmB,CAAC,CAiG9B"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { assignDomain, buildDomainIndex, DOMAIN_MD_FILENAME, parseDomainMd } from './chunk-MZV6WCF3.js';
|
|
2
2
|
import './chunk-JXT46QFC.js';
|
|
3
|
-
import './chunk-
|
|
3
|
+
import './chunk-5NZ56R63.js';
|
|
4
4
|
import './chunk-ZAP2BDHG.js';
|
|
5
|
-
import { factoryAssetRoots } from './chunk-
|
|
6
|
-
import { scanRepo } from './chunk-
|
|
5
|
+
import { factoryAssetRoots } from './chunk-7J7LQP6T.js';
|
|
6
|
+
import { scanRepo } from './chunk-ISA7R7PK.js';
|
|
7
7
|
import './chunk-P4VHQA4N.js';
|
|
8
8
|
import { walkDir, parseFrontmatter } from './chunk-BQCGFPHW.js';
|
|
9
9
|
import './chunk-GEEW6SY2.js';
|
|
@@ -64,7 +64,7 @@ async function libraryAssets() {
|
|
|
64
64
|
const domains = [];
|
|
65
65
|
try {
|
|
66
66
|
const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([
|
|
67
|
-
import('./open-library-
|
|
67
|
+
import('./open-library-ODW2PJ2J.js'),
|
|
68
68
|
import('./library/index.js'),
|
|
69
69
|
import('./core/index.js')
|
|
70
70
|
]);
|
|
@@ -78,9 +78,9 @@ async function libraryAssets() {
|
|
|
78
78
|
const entries = [];
|
|
79
79
|
for (const lib of libs) {
|
|
80
80
|
if (!existsSync(lib.path)) continue;
|
|
81
|
-
const libEntries = [...scanRepo(lib.path,
|
|
82
|
-
domains.push(...stampDomains(lib.path,
|
|
83
|
-
entries.push(
|
|
81
|
+
const libEntries = [...scanRepo(lib.path, lib.name)];
|
|
82
|
+
domains.push(...stampDomains(lib.path, lib.name, libEntries, false));
|
|
83
|
+
for (const entry of libEntries) entries.push({ origin: "library", entry, group: lib.name });
|
|
84
84
|
}
|
|
85
85
|
return { entries, domains, notes };
|
|
86
86
|
} finally {
|
|
@@ -97,7 +97,7 @@ async function libraryAssets() {
|
|
|
97
97
|
async function storeAssets(projectDir) {
|
|
98
98
|
const notes = [];
|
|
99
99
|
try {
|
|
100
|
-
const { resolveCatalogSource } = await import('./open-library-
|
|
100
|
+
const { resolveCatalogSource } = await import('./open-library-ODW2PJ2J.js');
|
|
101
101
|
const { source, close } = await resolveCatalogSource({ projectDir });
|
|
102
102
|
try {
|
|
103
103
|
const assets = await source.listAssets();
|
|
@@ -179,7 +179,7 @@ async function gatherAssetFeeds(am, projectDir) {
|
|
|
179
179
|
return {
|
|
180
180
|
entries: [
|
|
181
181
|
...src.entries,
|
|
182
|
-
...
|
|
182
|
+
...libs.entries,
|
|
183
183
|
...tag("factory", factory.entries),
|
|
184
184
|
...tag("store", store.entries)
|
|
185
185
|
],
|
|
@@ -201,5 +201,5 @@ function dedupeBySlug(domains) {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
export { gatherAssetFeeds };
|
|
204
|
-
//# sourceMappingURL=asset-feeds-
|
|
205
|
-
//# sourceMappingURL=asset-feeds-
|
|
204
|
+
//# sourceMappingURL=asset-feeds-PTZBWJEL.js.map
|
|
205
|
+
//# sourceMappingURL=asset-feeds-PTZBWJEL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/src/asset-feeds.ts"],"names":[],"mappings":"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","file":"asset-feeds-PTZBWJEL.js","sourcesContent":["/**\n * asset-feeds.ts — gather installable assets from every feed the manage TUI\n * surfaces.\n *\n * Four feeds, normalized into `CatalogEntry`:\n * - **Project sources** — `skaile.yaml` `sources:` entries (scanned via\n * `AssetManager.search()`; the new `sources:` field is merged into the\n * legacy repositories map in `AssetManager.loadConfig`).\n * - **Local libraries** — user authoring places from `LocalIndex` (filesystem\n * scan of each library's `path`).\n * - **Factory assets** — the bundled first-party `factory-assets` tree\n * (`factoryAssetRoots()`), scanned from disk. Always present; no network.\n * - **Remote store** — the configured catalog source when `catalog.url` is\n * not `local`. Failures are non-fatal — they surface as notes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n type AssetKind,\n type CatalogDomain,\n type CatalogEntry,\n factoryAssetRoots,\n parseFrontmatter,\n scanRepo,\n walkDir,\n} from \"@skaile/workspaces/core\";\nimport {\n assignDomain,\n buildDomainIndex,\n DOMAIN_MD_FILENAME,\n type DomainMd,\n parseDomainMd,\n} from \"@skaile/workspaces/discovery\";\n\n/** One non-fatal note returned to the caller for surfacing in the UI. */\nexport interface FeedNote {\n feed: \"sources\" | \"libraries\" | \"store\" | \"factory\";\n message: string;\n}\n\n/**\n * Which feed an asset came from. This is the top-level grouping axis in the\n * manage TUI (\"where is this skill coming from?\") and is intentionally distinct\n * from `publisher` (asset-intrinsic identity): the same publisher can legitimately\n * appear under more than one origin (e.g. a github repo you added as a `source`\n * that is also listed in the remote `store`).\n */\nexport type AssetOrigin = \"source\" | \"library\" | \"factory\" | \"store\";\n\n/** A catalog entry tagged with the feed it was gathered from. */\nexport interface OriginEntry {\n origin: AssetOrigin;\n entry: CatalogEntry;\n /**\n * Second-level grouping label for the manage tree. Defaults (when unset) to the\n * entry's `publisher`; the source feed sets it to the source's short repo name\n * so the Sources band groups by source — \"which repo did this come from\" — not\n * by the asset-intrinsic publisher.\n */\n group?: string;\n}\n\nexport interface AssetFeedResult {\n entries: OriginEntry[];\n /**\n * Display-only domain metadata, keyed (by the consumer) on `slug`. Populated\n * from the store's `catalog.listDomains` when the connected catalog source\n * exposes it; empty otherwise. The manage TUI joins `entry.domainSlug` → a\n * domain for grouping headers, descriptions, and relation badges.\n */\n domains: CatalogDomain[];\n notes: FeedNote[];\n}\n\n// ── Domain stamping (on-disk feeds) ──────────────────────────────────────────\n//\n// `scanRepo` (the source / library / factory scan path) does not read the\n// `DOMAIN.md` sidecars, so its entries arrive with no `domainSlug` and render\n// flat under their publisher band. We re-derive the grouping here from the\n// root's `DOMAIN.md` files (the same sidecars the remote store computes\n// server-side), and fall back to the repo/source name for any entry no domain\n// claims so it still groups instead of landing in the synthetic \"no domain\" pile.\n\n/** Repo-relative POSIX path (DOMAIN.md slugs are matched whole-segment, `/`-joined). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Parse every `DOMAIN.md` sidecar under `root` into a {@link DomainMd}. */\nfunction loadDomainMds(root: string): DomainMd[] {\n const out: DomainMd[] = [];\n for (const { path: filePath, name } of walkDir(root)) {\n if (name !== DOMAIN_MD_FILENAME) continue;\n try {\n const { data } = parseFrontmatter(readFileSync(filePath, \"utf8\"));\n out.push(parseDomainMd(toPosix(path.relative(root, filePath)), data));\n } catch {}\n }\n return out;\n}\n\n/**\n * Stamp `domainSlug` onto every entry scanned from `root` using the root's\n * `DOMAIN.md` sidecars. Returns the `CatalogDomain` registry for the real\n * domains, plus a synthetic fallback domain when any entry used the fallback.\n * Mutates `entries` in place (sets `entry.domainSlug`).\n *\n * `synthesizeFallback` (default true): entries no domain claims fall back to\n * `fallbackSlug` (the repo/source name). Pass `false` when the caller already\n * groups these entries one level up (e.g. the source feed groups by source\n * node) — unmatched entries then get no domain (`\"\"`) and hang directly under\n * that parent, instead of nesting under a same-named fallback domain.\n */\nfunction stampDomains(\n root: string,\n fallbackSlug: string,\n entries: CatalogEntry[],\n synthesizeFallback = true,\n): CatalogDomain[] {\n const domainMds = loadDomainMds(root);\n const assignables = entries.map((entry) => ({\n entry,\n ref: `${entry.kind}:${entry.name}`,\n relativePath: toPosix(path.relative(root, entry.source)),\n }));\n\n let usedFallback = false;\n for (const a of assignables) {\n const slug = assignDomain(a.relativePath, domainMds);\n if (slug) a.entry.domainSlug = slug;\n else if (synthesizeFallback) {\n a.entry.domainSlug = fallbackSlug;\n usedFallback = true;\n } else a.entry.domainSlug = \"\";\n }\n\n const { index } = buildDomainIndex(assignables, domainMds);\n const catalog: CatalogDomain[] = Object.values(index).map((d) => ({\n slug: d.slug,\n title: d.title,\n ...(d.description ? { description: d.description } : {}),\n ...(d.relation ? { relation: d.relation } : {}),\n ...(d.order !== undefined ? { order: d.order } : {}),\n ...(d.recommended[0] ? { recommendedRef: d.recommended[0] } : {}),\n }));\n if (usedFallback) catalog.push({ slug: fallbackSlug, title: fallbackSlug });\n return catalog;\n}\n\n/**\n * Pull assets from local libraries (the authoring places under\n * `~/.skaile/libraries/<name>/`, not the `~/.skaile/cache/sources/` cache). Each\n * library's `path` is scanned with `scanRepo` and grouped under the bare library\n * name (`group: <name>`) so the manage TUI shows a clean `<name>` band — no\n * `library:` prefix and no redundant same-named fallback domain (mirrors the\n * source feed).\n */\nasync function libraryAssets(): Promise<{\n entries: OriginEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const [{ openLibraryManager }, { skaileHomeDir }, { getGlobalCacheDir }] = await Promise.all([\n import(\"./open-library.ts\"),\n import(\"@skaile/workspaces/library\"),\n import(\"@skaile/workspaces/core\"),\n ]);\n const { manager, close } = await openLibraryManager();\n try {\n // Authoring libraries are the rows that are NOT source clones. index.db\n // rows predating the cache migration still carry the legacy\n // ~/.skaile/sources path, so exclude both roots.\n const newRoot = getGlobalCacheDir();\n const legacyRoot = path.join(skaileHomeDir(), \"sources\");\n const libs = (await manager.listLibraries()).filter(\n (l) => !l.path.startsWith(newRoot) && !l.path.startsWith(legacyRoot),\n );\n const entries: OriginEntry[] = [];\n for (const lib of libs) {\n if (!existsSync(lib.path)) continue;\n // Scan + group under the bare library name. `synthesizeFallback: false`\n // (as the source feed does) keeps domainless assets directly under the\n // library band instead of a redundant same-named fallback domain.\n const libEntries = [...scanRepo(lib.path, lib.name)];\n domains.push(...stampDomains(lib.path, lib.name, libEntries, false));\n for (const entry of libEntries) entries.push({ origin: \"library\", entry, group: lib.name });\n }\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n notes.push({\n feed: \"libraries\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/**\n * Pull a (best-effort) snapshot of the connected remote store catalog.\n *\n * - Returns no entries when the resolved catalog is local-only or the host\n * is unreachable (the failure becomes a note, not an exception).\n * - Maps `CatalogAsset` → `CatalogEntry`, carrying `publisher`, `repo`, and\n * `category` straight through and using `source = \"store://<id>\"`; per-file\n * `source` paths are not available for remote assets so the canonical ref is\n * a stable placeholder.\n */\nasync function storeAssets(projectDir: string): Promise<{\n entries: CatalogEntry[];\n domains: CatalogDomain[];\n notes: FeedNote[];\n}> {\n const notes: FeedNote[] = [];\n try {\n const { resolveCatalogSource } = await import(\"./open-library.ts\");\n const { source, close } = await resolveCatalogSource({ projectDir });\n try {\n const assets = await source.listAssets();\n const entries: CatalogEntry[] = assets.map((a) => ({\n name: a.name,\n kind: (a.kind as AssetKind) ?? \"skill\",\n description: a.description ?? \"\",\n source: `store://${a.id}`,\n publisher: a.publisher,\n repo: a.repo,\n domainSlug: a.domainSlug,\n category: a.category,\n version: a.version,\n requires: [],\n dependencies: [],\n }));\n // Domains are optional on the catalog source — only the store backend\n // (post Part 4 `catalog.listDomains`) exposes them. Duck-type the call so\n // older sources and the local catalog degrade to no domains, not an error.\n const domains = await loadDomains(source);\n return { entries, domains, notes };\n } finally {\n close();\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Local-only catalog throws a recognizable message — silence it; absence\n // is the user's choice, not an error.\n if (!/local/i.test(msg)) {\n notes.push({ feed: \"store\", message: msg });\n }\n return { entries: [], domains: [], notes };\n }\n}\n\n/** Optional `listDomains` capability — returns `[]` when the source lacks it. */\nasync function loadDomains(source: unknown): Promise<CatalogDomain[]> {\n const fn = (source as { listDomains?: () => Promise<CatalogDomain[]> }).listDomains;\n if (typeof fn !== \"function\") return [];\n const domains = await fn.call(source);\n return Array.isArray(domains) ? domains : [];\n}\n\n/** The slice of `AssetManager` the source feed needs (kept narrow for tests). */\ninterface AssetManagerLike {\n search: () => CatalogEntry[];\n searchRepoRoots: () => { repoName: string; root: string }[];\n}\n\n/**\n * Project-source feed: leans on `AssetManager.search()`, which now merges\n * `skaile.yaml`'s `sources:` field into the repositories map, then stamps\n * `DOMAIN.md`-derived domain grouping onto the scanned entries via\n * `searchRepoRoots()`.\n */\nfunction sourceAssets(am: AssetManagerLike): { entries: OriginEntry[]; domains: CatalogDomain[] } {\n // The bundled factory tree is injected into the repositories map as an implicit\n // source, so `am.search()` returns it too. It has its own `factory` origin band,\n // so drop it here — otherwise the same first-party skills list twice under Sources.\n const factoryPrefixes = factoryAssetRoots().map((r) => (r.endsWith(path.sep) ? r : r + path.sep));\n const isFactoryPath = (p: string) =>\n factoryPrefixes.some((fp) => p === fp.slice(0, -1) || p.startsWith(fp));\n\n const all = am.search().filter((e) => !isFactoryPath(e.source));\n const entries: OriginEntry[] = [];\n const domains: CatalogDomain[] = [];\n // Group each source's entries under the source node (its short repo name), not\n // the publisher: under Sources the navigational question is \"which repo did\n // this come from\". Each entry's `source` (absolute manifest path) lives under\n // exactly one declared root, so group + stamp by prefix. The cache slug is\n // URL-keyed `github.com/<org>/<repo>` — take the trailing segment.\n for (const { repoName, root } of am.searchRepoRoots()) {\n if (isFactoryPath(root)) continue;\n const prefix = root.endsWith(path.sep) ? root : root + path.sep;\n const owned = all.filter((e) => e.source === root || e.source.startsWith(prefix));\n if (owned.length === 0) continue;\n const sourceName = repoName.split(\"/\").pop() || repoName;\n // No synthetic fallback domain — the source node already groups these, so a\n // same-named fallback would just nest the source under itself.\n domains.push(...stampDomains(root, sourceName, owned, false));\n for (const entry of owned) entries.push({ origin: \"source\", entry, group: sourceName });\n }\n return { entries, domains };\n}\n\n/**\n * Factory-asset feed: scan the bundled first-party `factory-assets` tree(s)\n * resolved by `factoryAssetRoots()` (the `SKAILE_FACTORY_ASSETS_DIR` override\n * layer ahead of the bundled tree). Synchronous, no network — the entries are\n * the built-in skills / connectors / bundles that ship with `@skaile/workspaces`.\n * `factoryAssetRoots()` already dedupes, so the env override pointing at the\n * bundled dir collapses to one root.\n */\nfunction factoryAssets(): { entries: CatalogEntry[]; domains: CatalogDomain[]; notes: FeedNote[] } {\n const notes: FeedNote[] = [];\n const domains: CatalogDomain[] = [];\n try {\n const entries: CatalogEntry[] = [];\n for (const root of factoryAssetRoots()) {\n const rootEntries = [...scanRepo(root, \"skaile-factory\")];\n domains.push(...stampDomains(root, \"skaile-factory\", rootEntries));\n entries.push(...rootEntries);\n }\n return { entries, domains, notes };\n } catch (err) {\n notes.push({\n feed: \"factory\",\n message: err instanceof Error ? err.message : String(err),\n });\n return { entries: [], domains, notes };\n }\n}\n\n/** Tag a flat entry list with a single origin. */\nfunction tag(origin: AssetOrigin, entries: CatalogEntry[]): OriginEntry[] {\n return entries.map((entry) => ({ origin, entry }));\n}\n\n/**\n * Run all feeds and return a single normalized result.\n *\n * Feeds run in parallel where possible — the store fetch is the slow one\n * (one HTTP call) and is fired alongside the local library walk. The factory\n * scan is local + synchronous, so it runs inline.\n */\nexport async function gatherAssetFeeds(\n am: AssetManagerLike,\n projectDir: string,\n): Promise<AssetFeedResult> {\n const [libs, store] = await Promise.all([libraryAssets(), storeAssets(projectDir)]);\n const factory = factoryAssets();\n const src = sourceAssets(am);\n return {\n entries: [\n ...src.entries,\n ...libs.entries,\n ...tag(\"factory\", factory.entries),\n ...tag(\"store\", store.entries),\n ],\n // Store domains first (server-curated), then the on-disk feeds; dedupe by\n // slug, first-wins, so a store-served domain shadows a same-slug local one.\n domains: dedupeBySlug([...store.domains, ...src.domains, ...libs.domains, ...factory.domains]),\n notes: [...libs.notes, ...store.notes, ...factory.notes],\n };\n}\n\n/** First-wins dedupe of a domain list by `slug`. */\nfunction dedupeBySlug(domains: CatalogDomain[]): CatalogDomain[] {\n const seen = new Set<string>();\n const out: CatalogDomain[] = [];\n for (const d of domains) {\n if (seen.has(d.slug)) continue;\n seen.add(d.slug);\n out.push(d);\n }\n return out;\n}\n"]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, softDepName } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, NON_DEPLOYABLE_KINDS, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, softDepName } from '../chunk-MY7TSVO7.js';
|
|
2
|
+
import '../chunk-MWE7DEGC.js';
|
|
3
3
|
import '../chunk-MZV6WCF3.js';
|
|
4
4
|
import '../chunk-JXT46QFC.js';
|
|
5
5
|
import '../chunk-UKJMQZV6.js';
|
|
6
6
|
import '../chunk-R7FOF242.js';
|
|
7
|
-
export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-
|
|
7
|
+
export { createScaffold, deployAll, installAgent, removeAsset } from '../chunk-STQOPWPS.js';
|
|
8
8
|
export { BUILT_IN_FRAGMENTS, loadPromptExtensions, resolveFragments } from '../chunk-CGYEHQOX.js';
|
|
9
|
-
import '../chunk-
|
|
9
|
+
import '../chunk-5NZ56R63.js';
|
|
10
10
|
import '../chunk-ZAP2BDHG.js';
|
|
11
|
-
import '../chunk-
|
|
12
|
-
import '../chunk-
|
|
11
|
+
import '../chunk-7J7LQP6T.js';
|
|
12
|
+
import '../chunk-ISA7R7PK.js';
|
|
13
13
|
import '../chunk-P4VHQA4N.js';
|
|
14
14
|
import '../chunk-BQCGFPHW.js';
|
|
15
15
|
import '../chunk-GEEW6SY2.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-
|
|
1
|
+
export { createScaffold, deployAll, deployAsset, removeAsset } from '../chunk-STQOPWPS.js';
|
|
2
2
|
import '../chunk-CGYEHQOX.js';
|
|
3
|
-
import '../chunk-
|
|
3
|
+
import '../chunk-5NZ56R63.js';
|
|
4
4
|
import '../chunk-ZAP2BDHG.js';
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-7J7LQP6T.js';
|
|
6
|
+
import '../chunk-ISA7R7PK.js';
|
|
7
7
|
import '../chunk-P4VHQA4N.js';
|
|
8
8
|
import '../chunk-BQCGFPHW.js';
|
|
9
9
|
import '../chunk-GEEW6SY2.js';
|