@skaile/workspaces 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/README.md +1 -1
- package/dist/app-sdk/index.d.ts +2 -0
- package/dist/app-sdk/index.js +295 -0
- package/dist/app-sdk/index.js.map +1 -0
- package/dist/app-sdk/src/app.d.ts +101 -0
- package/dist/app-sdk/src/app.d.ts.map +1 -0
- package/dist/app-sdk/src/define-app-capability.d.ts +139 -0
- package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -0
- package/dist/app-sdk/src/index.d.ts +16 -0
- package/dist/app-sdk/src/index.d.ts.map +1 -0
- package/dist/app-sdk/src/logger.d.ts +26 -0
- package/dist/app-sdk/src/logger.d.ts.map +1 -0
- package/dist/app-sdk/src/transport.d.ts +84 -0
- package/dist/app-sdk/src/transport.d.ts.map +1 -0
- package/dist/app-sdk/src/ws-transport.d.ts +63 -0
- package/dist/app-sdk/src/ws-transport.d.ts.map +1 -0
- package/dist/{asset-feeds-NEZKG6YS.js → asset-feeds-NG5TKB3V.js} +8 -8
- package/dist/{asset-feeds-NEZKG6YS.js.map → asset-feeds-NG5TKB3V.js.map} +1 -1
- package/dist/asset-manager/index.js +7 -7
- package/dist/asset-manager/installer.js +6 -6
- package/dist/asset-manager/renderers.js +2 -2
- package/dist/asset-manager/src/index.d.ts +42 -6
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/bridge/drivers/claude-sdk.js +2 -2
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +3 -3
- package/dist/{chunk-AZAS3FKN.js → chunk-26YCXWCU.js} +48 -2
- package/dist/chunk-26YCXWCU.js.map +1 -0
- package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
- package/dist/chunk-4N5XR5DQ.js.map +1 -0
- package/dist/{chunk-RHMQPLIK.js → chunk-77OXU44H.js} +6 -6
- package/dist/{chunk-RHMQPLIK.js.map → chunk-77OXU44H.js.map} +1 -1
- package/dist/{chunk-7CNIRF42.js → chunk-AAGD2J4H.js} +20 -11
- package/dist/chunk-AAGD2J4H.js.map +1 -0
- package/dist/{chunk-BZMTR5WW.js → chunk-B6SSXVF2.js} +4 -4
- package/dist/chunk-B6SSXVF2.js.map +1 -0
- package/dist/{chunk-YOPSHV7V.js → chunk-BE5KTS2L.js} +3 -3
- package/dist/{chunk-YOPSHV7V.js.map → chunk-BE5KTS2L.js.map} +1 -1
- package/dist/{chunk-JQMIG7D3.js → chunk-BJHZHNKR.js} +3 -3
- package/dist/{chunk-JQMIG7D3.js.map → chunk-BJHZHNKR.js.map} +1 -1
- package/dist/{chunk-5XS3BFJS.js → chunk-DNFIKRO6.js} +3 -3
- package/dist/{chunk-5XS3BFJS.js.map → chunk-DNFIKRO6.js.map} +1 -1
- package/dist/{chunk-PSMW5HLX.js → chunk-GWDZW44L.js} +4 -4
- package/dist/{chunk-PSMW5HLX.js.map → chunk-GWDZW44L.js.map} +1 -1
- package/dist/{chunk-K5SD7FSF.js → chunk-HKX5VSWQ.js} +3 -3
- package/dist/{chunk-K5SD7FSF.js.map → chunk-HKX5VSWQ.js.map} +1 -1
- package/dist/{chunk-P3MJYPZE.js → chunk-HWTQ6HPO.js} +4 -4
- package/dist/{chunk-P3MJYPZE.js.map → chunk-HWTQ6HPO.js.map} +1 -1
- package/dist/{chunk-XKARS6EE.js → chunk-IEX5YV4R.js} +3 -3
- package/dist/{chunk-XKARS6EE.js.map → chunk-IEX5YV4R.js.map} +1 -1
- package/dist/{chunk-GB4SEEK4.js → chunk-IRORTHGA.js} +10 -4
- package/dist/chunk-IRORTHGA.js.map +1 -0
- package/dist/{chunk-YXEPZ7LP.js → chunk-JZPEEMIM.js} +5 -5
- package/dist/{chunk-YXEPZ7LP.js.map → chunk-JZPEEMIM.js.map} +1 -1
- package/dist/{chunk-WFEPI5LA.js → chunk-LCTRCWBK.js} +5 -5
- package/dist/{chunk-WFEPI5LA.js.map → chunk-LCTRCWBK.js.map} +1 -1
- package/dist/{chunk-QK5JDAFF.js → chunk-NKTFEV7Q.js} +4 -4
- package/dist/{chunk-QK5JDAFF.js.map → chunk-NKTFEV7Q.js.map} +1 -1
- package/dist/{chunk-ETYDLXI5.js → chunk-OSCJRUKM.js} +2 -2
- package/dist/{chunk-ETYDLXI5.js.map → chunk-OSCJRUKM.js.map} +1 -1
- package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
- package/dist/chunk-PK7Q7MC7.js.map +1 -0
- package/dist/{chunk-RYRJEVQ4.js → chunk-QM2TT2PJ.js} +19 -11
- package/dist/chunk-QM2TT2PJ.js.map +1 -0
- package/dist/{chunk-SUOGVHGJ.js → chunk-TNSCHS4I.js} +2 -2
- package/dist/{chunk-SUOGVHGJ.js.map → chunk-TNSCHS4I.js.map} +1 -1
- package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
- package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
- package/dist/{chunk-RJ3HUSBC.js → chunk-UQ5FK2AB.js} +3 -3
- package/dist/{chunk-RJ3HUSBC.js.map → chunk-UQ5FK2AB.js.map} +1 -1
- package/dist/{chunk-OBSSORCE.js → chunk-X57AMTAH.js} +72 -9
- package/dist/chunk-X57AMTAH.js.map +1 -0
- package/dist/cli/index.js +766 -185
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/preset-apply.d.ts +59 -0
- package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
- package/dist/cli/src/commands/preset-cmd.d.ts +2 -2
- package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/preset-export.d.ts +93 -0
- package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/why.d.ts +18 -0
- package/dist/cli/src/commands/why.d.ts.map +1 -1
- package/dist/connectors/config.js +2 -2
- package/dist/connectors/index.js +4 -4
- package/dist/core/index.js +2 -2
- package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
- package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
- package/dist/deploy/index.js +2 -2
- package/dist/discovery/index.js +2 -2
- package/dist/{ensure-sources-4BAJGJRK.js → ensure-sources-CHC7X7AJ.js} +10 -10
- package/dist/{ensure-sources-4BAJGJRK.js.map → ensure-sources-CHC7X7AJ.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +4 -4
- package/dist/factory-assets/connectors/deploy.js +4 -4
- package/dist/factory-assets/connectors/devserver.js +4 -4
- package/dist/factory-assets/connectors/flow/adapter.js +4 -4
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +3 -3
- package/dist/factory-assets/connectors/flow/run-flow.js +5 -5
- package/dist/factory-assets/connectors/flow.js +4 -4
- package/dist/factory-assets/connectors/git.js +4 -4
- package/dist/factory-assets/connectors/gmail.js +4 -4
- package/dist/factory-assets/connectors/googledrive.js +4 -4
- package/dist/factory-assets/connectors/local.js +4 -4
- package/dist/factory-assets/connectors/mattermost.js +4 -4
- package/dist/factory-assets/connectors/memory.js +4 -4
- package/dist/factory-assets/connectors/minio.js +4 -4
- package/dist/factory-assets/connectors/postgres.js +4 -4
- package/dist/factory-assets/connectors/s3.js +4 -4
- package/dist/factory-assets/connectors/sharepoint.js +4 -4
- package/dist/factory-assets/connectors/sqlite.js +4 -4
- package/dist/factory-assets/connectors/static-server.js +4 -4
- package/dist/factory-assets/connectors/tunnel.js +4 -4
- package/dist/factory-assets/connectors/webdav.js +4 -4
- package/dist/factory-assets/connectors/xstate-store.js +4 -4
- package/dist/factory-assets/connectors/xstate.js +4 -4
- package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
- package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
- package/dist/{flows-UFCOGAAM.js → flows-CGOCUNO6.js} +4 -4
- package/dist/{flows-UFCOGAAM.js.map → flows-CGOCUNO6.js.map} +1 -1
- package/dist/{helpers-2OL4FRFB.js → helpers-GWG4CYYC.js} +10 -10
- package/dist/{helpers-2OL4FRFB.js.map → helpers-GWG4CYYC.js.map} +1 -1
- package/dist/library/index.js +6 -6
- package/dist/library/install.js +2 -2
- package/dist/{open-library-RPV2LIKL.js → open-library-37PXN6MK.js} +15 -15
- package/dist/{open-library-RPV2LIKL.js.map → open-library-37PXN6MK.js.map} +1 -1
- package/dist/{paths-ML33B5SB.js → paths-UVOHWUPY.js} +4 -4
- package/dist/{paths-ML33B5SB.js.map → paths-UVOHWUPY.js.map} +1 -1
- package/dist/{plugin-store-G4HKWCTU.js → plugin-store-EIMVEH3O.js} +4 -4
- package/dist/{plugin-store-G4HKWCTU.js.map → plugin-store-EIMVEH3O.js.map} +1 -1
- package/dist/runner/index.js +10 -10
- package/dist/runner/src/capability-registry.d.ts +15 -4
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +7 -7
- package/dist/sdk/bridge.js +3 -3
- package/dist/sdk/core.js +2 -2
- package/dist/sdk/flow.js +3 -3
- package/dist/sdk/index.js +10 -10
- package/dist/sdk/runner.js +10 -10
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-WS6PF6JQ.js → setup-DINVR6ZQ.js} +7 -7
- package/dist/{setup-WS6PF6JQ.js.map → setup-DINVR6ZQ.js.map} +1 -1
- package/dist/{source-22U5NOY4.js → source-RXAHIGMD.js} +16 -16
- package/dist/{source-22U5NOY4.js.map → source-RXAHIGMD.js.map} +1 -1
- package/dist/{store-client-YLMBH5MF.js → store-client-T3F663YG.js} +5 -5
- package/dist/{store-client-YLMBH5MF.js.map → store-client-T3F663YG.js.map} +1 -1
- package/dist/tui/index.js +10 -10
- package/dist/types/index.js +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/capabilities.d.ts +8 -2
- package/dist/types/src/capabilities.d.ts.map +1 -1
- package/dist/types/src/logging.d.ts +3 -2
- package/dist/types/src/logging.d.ts.map +1 -1
- package/dist/types/src/manifests/_shared.d.ts +4 -4
- package/dist/types/src/manifests/index.d.ts +2 -2
- package/dist/types/src/manifests/index.d.ts.map +1 -1
- package/dist/types/src/manifests/preset.d.ts +10 -0
- package/dist/types/src/manifests/preset.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +11 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +2 -2
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +7 -1
- package/dist/chunk-7CNIRF42.js.map +0 -1
- package/dist/chunk-AZAS3FKN.js.map +0 -1
- package/dist/chunk-BZMTR5WW.js.map +0 -1
- package/dist/chunk-EVZKWY5J.js.map +0 -1
- package/dist/chunk-GB4SEEK4.js.map +0 -1
- package/dist/chunk-OBSSORCE.js.map +0 -1
- package/dist/chunk-RYRJEVQ4.js.map +0 -1
- package/dist/chunk-SKADPHAM.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,127 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#325](https://github.com/skaile-ai/workspaces/pull/325) [`a91d49c`](https://github.com/skaile-ai/workspaces/commit/a91d49cba353e1714e97faaa3fb7dde21a40304c) Thanks [@henkbla](https://github.com/henkbla)! - Protocol v3.4.0 — add the embedded-app capability source to the wire types.
|
|
8
|
+
`CapabilityOrigin` gains `{ kind: 'app'; appId: string }` and `Capability.side`
|
|
9
|
+
gains `'app'` ("the handler runs in the embedded app"). Both are additive and
|
|
10
|
+
ride through under the protocol's major-equality rule — a peer that does not
|
|
11
|
+
understand `origin:'app'` / `side:'app'` never produces or routes them, so
|
|
12
|
+
existing consumers (runner, bridge, platform) are unaffected until a source
|
|
13
|
+
actually registers an `app` capability. No new message types:
|
|
14
|
+
`capability_register` / `capability_invoked` / `capability_result` /
|
|
15
|
+
`capability_approve` / `render_invoked` are reused verbatim.
|
|
16
|
+
|
|
17
|
+
`PROTOCOL_VERSION` bumps to `3.4.0`. `isUserInvokable` defaults `app`-origin
|
|
18
|
+
capabilities to LLM-only (`false`) unless the author sets
|
|
19
|
+
`audience: ['llm','user']`. The `capability` log subkind now recognizes `app`
|
|
20
|
+
(with `appId` encoded into `instance` as `<appId>.<name>`).
|
|
21
|
+
|
|
22
|
+
Design: `packages/workspaces/_devlog/specs/2026-07-07-app-capability-source.md`.
|
|
23
|
+
This is the protocol keystone for Skailify (embedding agent-controllable apps in
|
|
24
|
+
Skaile sessions); the runner trust guard and platform-gateway routing land in
|
|
25
|
+
follow-up changes.
|
|
26
|
+
|
|
27
|
+
- [#326](https://github.com/skaile-ai/workspaces/pull/326) [`8f5e6e6`](https://github.com/skaile-ai/workspaces/commit/8f5e6e6f0fe214df35418899e3eb8747fe10fc60) Thanks [@henkbla](https://github.com/henkbla)! - Extend the runner capability-registry origin-trust guard for the `app`
|
|
28
|
+
capability origin (protocol 3.4.0). `RegisterSource` gains an
|
|
29
|
+
`{ kind: 'app'; appId: string }` variant, and `CapabilityRegistry.register`
|
|
30
|
+
now enforces:
|
|
31
|
+
|
|
32
|
+
- an **app source** may declare `origin.kind: 'app'` **only for its own `appId`**;
|
|
33
|
+
- **no other source** (agent, client, skill, connector, mount, flow, mcp,
|
|
34
|
+
framework) may declare an `app` origin — the `agent` rule is tightened to
|
|
35
|
+
exclude `app` so a skill/connector/flow cannot impersonate an embedded app.
|
|
36
|
+
|
|
37
|
+
Violations are dropped with a warn-level log, exactly as origin mismatches are
|
|
38
|
+
today. This closes the forgery hole described in the app-capability-source spec
|
|
39
|
+
(§ Trust / forgery rule): without it a compromised source could register
|
|
40
|
+
`app.delete_everything` under a real app's id. The bare-string `'client'` /
|
|
41
|
+
`'agent'` sources and all existing origin rules are unchanged.
|
|
42
|
+
|
|
43
|
+
Also makes the registry fully `app`-aware in lock-step: `ORIGIN_PROMPT_ORDER`
|
|
44
|
+
and `CapabilityListFilter.side` gain `'app'`, so an `app`-origin capability's
|
|
45
|
+
`promptFragment` reaches the `<CAPABILITIES>` section and `list({ side: 'app' })`
|
|
46
|
+
is expressible. The authoritative per-connection `appId` binding is enforced at
|
|
47
|
+
the platform gateway (follow-up); this is the runner-side defence-in-depth.
|
|
48
|
+
|
|
49
|
+
- [#327](https://github.com/skaile-ai/workspaces/pull/327) [`fc4ed2f`](https://github.com/skaile-ai/workspaces/commit/fc4ed2fa8945484a329f5c1f5f04d9a994cb9694) Thanks [@henkbla](https://github.com/henkbla)! - Add `@skaile/workspaces/app-sdk` — the thin SDK an embedded, agent-controllable
|
|
50
|
+
app uses to become a capability peer inside a Skaile session (the "Skailify"
|
|
51
|
+
embed-in-platform slice; protocol 3.4.0).
|
|
52
|
+
|
|
53
|
+
`createSkaileApp({ appId, transport })` opens an authenticated back-connection,
|
|
54
|
+
performs the app handshake (`app_hello` → `app_hello_ack` binding the session to
|
|
55
|
+
`appId`, or `app_hello_nack` refusal), registers `app.*` capabilities stamped
|
|
56
|
+
with `origin:{kind:'app', appId}` / `side:'app'`, and answers inbound
|
|
57
|
+
`capability_invoked` events by dispatching to the local handler and replying with
|
|
58
|
+
`capability_result` (a thrown handler / invalid input / unknown name rides back
|
|
59
|
+
as `{ error }`). Register / deregister mid-session is supported.
|
|
60
|
+
|
|
61
|
+
Also exports `defineAppCapability` (Zod-typed authoring that fixes `side:'app'`),
|
|
62
|
+
the `AppTransport` interface + app-channel message types, a browser-safe
|
|
63
|
+
`createWebSocketAppTransport` (injected session identity presented as the
|
|
64
|
+
`skaile-bearer.*` subprotocol the gateway already decodes), and an `AppLogger`.
|
|
65
|
+
|
|
66
|
+
Purely additive: a new subpath export; existing consumers are unaffected. No new
|
|
67
|
+
capability wire types — `capability_register` / `capability_invoked` /
|
|
68
|
+
`capability_result` are reused verbatim; only the `app_hello` handshake is
|
|
69
|
+
specific to this app↔gateway channel. Design:
|
|
70
|
+
`packages/workspaces/_devlog/specs/2026-07-07-app-capability-source.md`.
|
|
71
|
+
|
|
72
|
+
- [#314](https://github.com/skaile-ai/workspaces/pull/314) [`d608fd9`](https://github.com/skaile-ai/workspaces/commit/d608fd9e7517662327d48123aad5a3ad080b1232) Thanks [@mortegro](https://github.com/mortegro)! - `FlowNode.type` now includes `"sub-flow"` and `"router"` alongside `"skill"` and
|
|
73
|
+
`"group"`, matching node kinds already shipped in production flows (e.g.
|
|
74
|
+
appbuilder-standard/-complex). `FlowNode.data` gained `flow` (delegated flow ID
|
|
75
|
+
for `sub-flow` nodes) and `routes` (ordered `{condition, target}` table for
|
|
76
|
+
`router` nodes). Consumers that previously cast around the narrower union can
|
|
77
|
+
drop those casts.
|
|
78
|
+
|
|
79
|
+
This is a type-level and documentation change only — the engine
|
|
80
|
+
(`computeFlowState`, `computeFlowStateFromSnapshots`, `computeSkippable`) still
|
|
81
|
+
tracks only `'skill'` nodes; `'sub-flow'` and `'router'` are excluded from
|
|
82
|
+
dependency computation the same as `'group'` today. See
|
|
83
|
+
`docs/flow-execution.md#node-types` for the full semantics of each kind and
|
|
84
|
+
what's intentionally left as downstream/future work.
|
|
85
|
+
|
|
86
|
+
- [#317](https://github.com/skaile-ai/workspaces/pull/317) [`ca173c9`](https://github.com/skaile-ai/workspaces/commit/ca173c9e1617d45051a1d7fa21712660250fe13b) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Add `skaile preset apply <path>` — the consume half of the preset roundtrip. It reads a `.preset.yaml`, resolves placeholders, and writes the items back additively into the target project's `skaile.yaml` (the inverse of `preset export`). Bare-ref items become dependencies (deduped), `connector:<driver>` items become `ConnectorDeclaration`s, and `mcp:<id>` items become `McpServerDeclaration`s; a connector/mcp whose id already exists and differs is skipped (the user's config wins). Placeholder resolution is secret-safe: a `secret` placeholder resolves to an `env:<KEY>` reference (never the raw value) and the key is surfaced in a "set these env vars" summary, while non-secret `${INPUT:KEY}` tokens inline the provided literal (from `--input KEY=VAL`, an interactive prompt, or a declared default). Nested-preset refs are rejected for now. Unless `--no-deploy` is passed, the command runs the project install so new skill deps deploy. Flags: `--project-dir`, `--input` (repeatable), `--yes`, `--no-deploy`. Also adds `WorkspaceYamlEditor.addDependency` (dedupe) and `WorkspaceYamlEditor.setMcpServer` (add/replace-by-id) comment-preserving methods.
|
|
87
|
+
|
|
88
|
+
- [#316](https://github.com/skaile-ai/workspaces/pull/316) [`9c2a7ab`](https://github.com/skaile-ai/workspaces/commit/9c2a7abea4076bb79ce22b9b38f3885b7d75a513) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Add `skaile preset export [name]` — snapshot a workspace's own `skaile.yaml` into a shareable `.preset.yaml`. Dependencies map to bare-ref items (copied verbatim), connectors to `connector:<driver>` items, and MCP servers to `mcp:<id>` items; `agent_config` is deliberately excluded as personal/machine-specific. The command has first-class secret safety: it scans connector/mcp config for inline credentials (anything that is not an `env:`/`forge:`/`secret:`/… reference), prints a loud summary, redacts flagged values into `${INPUT:…}` placeholders under `--redact` (or interactively on a TTY), and stamps a `CONTAINS INLINE SECRETS` header when any inline secret is left in place — so an export is never a silent success. Flags: `--project-dir`, `--out`, `--dir`, `--redact`, `--force`.
|
|
89
|
+
|
|
90
|
+
- [#318](https://github.com/skaile-ai/workspaces/pull/318) [`c5c145c`](https://github.com/skaile-ai/workspaces/commit/c5c145ca06627da766b6df1471d626403daf3c0b) Thanks [@P1umPudding](https://github.com/P1umPudding)! - `skaile preset export`/`apply` now carry the **sources** a preset's dependencies resolve from, so a preset built against a non-factory git source (e.g. `skill:@mattpocock/grill-me` from `mattpocock/skills`) resolves on `apply` even in a workspace that never registered that source. Export captures only the needed sources — each dependency is matched to its lock entry by version-agnostic identity (kind+publisher+name) and the entry's `source.url` is recorded, excluding the bundled factory source; with no lock (or an unresolved dependency) it conservatively records every non-factory source. Apply registers those sources into `skaile.yaml` (deduped by url; an existing url with a differing pin is left untouched) before running install. Adds an optional `sources` field to the preset manifest schema and exports `FACTORY_SOURCE_URL` / `normalizeRepoUrl` from `@skaile/workspaces/asset-manager`.
|
|
91
|
+
|
|
92
|
+
### Patch Changes
|
|
93
|
+
|
|
94
|
+
- [#300](https://github.com/skaile-ai/workspaces/pull/300) [`5824f67`](https://github.com/skaile-ai/workspaces/commit/5824f671f3ca9c88adcebfe3584c03360f67e1b0) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Fix a hard crash in `skaile doctor`: `AssetManager.info()` now tolerates a bare
|
|
95
|
+
`kind:name` ref (publisher optional) instead of throwing "publisher required".
|
|
96
|
+
`doctor()` iterates `listDeployed()`, which — because it scans deploy dirs —
|
|
97
|
+
emits publisher-less refs; the strict parse in front of `info()`'s already
|
|
98
|
+
publisher-optional matching crashed on the first deployed asset. `info()` falls
|
|
99
|
+
back to a minimal tolerant parse and returns `null` when it can recover neither
|
|
100
|
+
a kind nor a name; the strict `parseAssetRef` is unchanged for other callers.
|
|
101
|
+
|
|
102
|
+
- [#298](https://github.com/skaile-ai/workspaces/pull/298) [`80c9d58`](https://github.com/skaile-ai/workspaces/commit/80c9d5844f8c06a89769bc7c411b69c6a63eca9b) Thanks [@P1umPudding](https://github.com/P1umPudding)! - Fix `skaile install` never pruning manifest-less (content-sha pseudo-version)
|
|
103
|
+
assets. The install reconcile derived asset identity via `parseCanonicalLockKey`,
|
|
104
|
+
which rejected `#0.0.0-sha.<hash>` lock keys and returned `{}`, so a dependency
|
|
105
|
+
removed from `skaile.yaml` left its deployed files behind as a silent orphan.
|
|
106
|
+
`parseCanonicalLockKey` now falls back to parsing the bare identity when the pin
|
|
107
|
+
is a non-canonical pseudo-version — fixing the reconcile plus every other lock-key
|
|
108
|
+
consumer (locked-install restore, `list`/`globalState` state machine, `outdated`)
|
|
109
|
+
for manifest-less assets. `add`/`remove` now also keep `skaile.lock.yaml` in sync
|
|
110
|
+
so an added-then-removed asset can be reconciled away, and the reconcile warns on a
|
|
111
|
+
genuinely unparseable lock key instead of skipping it silently.
|
|
112
|
+
|
|
113
|
+
- [#309](https://github.com/skaile-ai/workspaces/pull/309) [`f7dbdf3`](https://github.com/skaile-ai/workspaces/commit/f7dbdf341cf5f6aa6289d6d971a32183ac1cdcf3) Thanks [@mortegro](https://github.com/mortegro)! - `skaile source add` now clones/validates the repo **before** writing the source
|
|
114
|
+
entry to `skaile.yaml`. Previously the entry was persisted first, so a failed
|
|
115
|
+
clone (repo not found, bad URL, network error) — which hard-exits in
|
|
116
|
+
`ensureClone` — left a stale, un-cloned source recorded in `skaile.yaml`. The
|
|
117
|
+
entry is only written once the source is known-good.
|
|
118
|
+
|
|
119
|
+
A `--force` re-clone now moves the existing clone aside and only deletes it once
|
|
120
|
+
the fresh clone succeeds, so a failed re-clone restores the previous clone
|
|
121
|
+
instead of leaving the source registered with no cache behind it.
|
|
122
|
+
|
|
123
|
+
- [#299](https://github.com/skaile-ai/workspaces/pull/299) [`f91a0dd`](https://github.com/skaile-ai/workspaces/commit/f91a0dd14f5775b76457c79f0d7ff68785fc3180) Thanks [@P1umPudding](https://github.com/P1umPudding)! - `skaile why <ref>` now distinguishes an undeclared ref from a declared-but-uninstalled one when the dependency chain is empty. Previously it always printed "not found in lock file. Run `skaile install` first." — misleading when nothing in `skaile.yaml` requires the ref, since install cannot help. Undeclared refs now get a clearer message pointing to `skaile list`; declared-but-uninstalled refs keep the install hint. The empty-chain decision is factored into a pure exported `whyEmptyMessage` helper.
|
|
124
|
+
|
|
3
125
|
## 1.3.0
|
|
4
126
|
|
|
5
127
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -77,6 +77,6 @@ bun run cli run mvp # run a flow
|
|
|
77
77
|
## Architecture Docs
|
|
78
78
|
|
|
79
79
|
- [Client-Server Architecture](../../docs/client-server-architecture.md) — design for the agent client, transport, and shared state protocol (new packages: `client`, `transport`)
|
|
80
|
-
- [Session Dispatcher Architecture](../../docs/session/architecture.md) — design for session message dispatching, persistence, and fan-out (new package: `session`)
|
|
80
|
+
- [Session Dispatcher Architecture](../../docs/sections/session/architecture.md) — design for session message dispatching, persistence, and fan-out (new package: `session`)
|
|
81
81
|
|
|
82
82
|
See each package's `CLAUDE.md` for architecture details and developer conventions.
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION_STRING } from '../chunk-PK7Q7MC7.js';
|
|
2
|
+
import '../chunk-NSBPE2FW.js';
|
|
3
|
+
import * as z from 'zod';
|
|
4
|
+
|
|
5
|
+
// app-sdk/src/logger.ts
|
|
6
|
+
var noopLogger = {
|
|
7
|
+
debug() {
|
|
8
|
+
},
|
|
9
|
+
info() {
|
|
10
|
+
},
|
|
11
|
+
warn() {
|
|
12
|
+
},
|
|
13
|
+
error() {
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var consoleLogger = {
|
|
17
|
+
debug(message, data) {
|
|
18
|
+
console.debug(`[skaile-app] ${message}`, ...data !== void 0 ? [data] : []);
|
|
19
|
+
},
|
|
20
|
+
info(message, data) {
|
|
21
|
+
console.info(`[skaile-app] ${message}`, ...data !== void 0 ? [data] : []);
|
|
22
|
+
},
|
|
23
|
+
warn(message, data) {
|
|
24
|
+
console.warn(`[skaile-app] ${message}`, ...data !== void 0 ? [data] : []);
|
|
25
|
+
},
|
|
26
|
+
error(message, data) {
|
|
27
|
+
console.error(`[skaile-app] ${message}`, ...data !== void 0 ? [data] : []);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// app-sdk/src/app.ts
|
|
32
|
+
var DEFAULT_HELLO_TIMEOUT_MS = 1e4;
|
|
33
|
+
function createSkaileApp(options) {
|
|
34
|
+
const {
|
|
35
|
+
appId,
|
|
36
|
+
transport,
|
|
37
|
+
protocolVersion = PROTOCOL_VERSION_STRING,
|
|
38
|
+
helloTimeoutMs = DEFAULT_HELLO_TIMEOUT_MS,
|
|
39
|
+
logger = noopLogger
|
|
40
|
+
} = options;
|
|
41
|
+
const registry = /* @__PURE__ */ new Map();
|
|
42
|
+
let session = null;
|
|
43
|
+
let unsubscribeMessage = null;
|
|
44
|
+
let unsubscribeDisconnect = null;
|
|
45
|
+
let abortHandshake = null;
|
|
46
|
+
function toWire(cap) {
|
|
47
|
+
return {
|
|
48
|
+
name: cap.name,
|
|
49
|
+
displayName: cap.displayName,
|
|
50
|
+
description: cap.description,
|
|
51
|
+
inputSchema: cap.inputSchema,
|
|
52
|
+
outputSchema: cap.outputSchema,
|
|
53
|
+
side: "app",
|
|
54
|
+
origin: { kind: "app", appId },
|
|
55
|
+
scope: cap.scope,
|
|
56
|
+
requiresApproval: cap.requiresApproval,
|
|
57
|
+
kind: cap.kind,
|
|
58
|
+
audience: cap.audience,
|
|
59
|
+
promptFragment: cap.promptFragment,
|
|
60
|
+
callTimeoutMs: cap.callTimeoutMs,
|
|
61
|
+
render: cap.render
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function registerCapability(cap) {
|
|
65
|
+
registry.set(cap.name, cap);
|
|
66
|
+
if (session) {
|
|
67
|
+
transport.send({ type: "capability_register", capabilities: [toWire(cap)] });
|
|
68
|
+
logger.debug("registered capability (live)", { name: cap.name });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function deregisterCapability(name) {
|
|
72
|
+
if (!registry.delete(name)) return;
|
|
73
|
+
if (session) {
|
|
74
|
+
transport.send({ type: "capability_deregister", names: [name] });
|
|
75
|
+
logger.debug("deregistered capability (live)", { name });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function handleInvocation(callId, name, input) {
|
|
79
|
+
const cap = registry.get(name);
|
|
80
|
+
if (!cap) {
|
|
81
|
+
transport.send({
|
|
82
|
+
type: "capability_result",
|
|
83
|
+
callId,
|
|
84
|
+
result: { error: `Unknown app capability: ${name}` }
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const validated = cap.inputZod.parse(input);
|
|
90
|
+
const ctx = { sessionId: session?.sessionId ?? "", appId, log: logger };
|
|
91
|
+
const result = await cap.handler(validated, ctx);
|
|
92
|
+
transport.send({ type: "capability_result", callId, result });
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
95
|
+
logger.warn("capability handler failed", { name, error: message });
|
|
96
|
+
transport.send({ type: "capability_result", callId, result: { error: message } });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function connect() {
|
|
100
|
+
if (session) return Promise.resolve(session);
|
|
101
|
+
if (abortHandshake) return Promise.reject(new Error("app connect() is already in progress"));
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
let settled = false;
|
|
104
|
+
let helloTimer;
|
|
105
|
+
const finishHandshake = () => {
|
|
106
|
+
if (helloTimer) clearTimeout(helloTimer);
|
|
107
|
+
abortHandshake = null;
|
|
108
|
+
};
|
|
109
|
+
const fail = (err) => {
|
|
110
|
+
if (settled) return;
|
|
111
|
+
settled = true;
|
|
112
|
+
finishHandshake();
|
|
113
|
+
reject(err);
|
|
114
|
+
};
|
|
115
|
+
abortHandshake = fail;
|
|
116
|
+
const onMessage = (message) => {
|
|
117
|
+
switch (message.type) {
|
|
118
|
+
case "app_hello_ack": {
|
|
119
|
+
if (settled) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
finishHandshake();
|
|
122
|
+
session = { sessionId: message.sessionId, userId: message.userId };
|
|
123
|
+
logger.info("app connection established", { appId, sessionId: session.sessionId });
|
|
124
|
+
const wire = Array.from(registry.values()).map(toWire);
|
|
125
|
+
if (wire.length > 0) {
|
|
126
|
+
transport.send({ type: "capability_register", capabilities: wire });
|
|
127
|
+
}
|
|
128
|
+
resolve(session);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
case "app_hello_nack":
|
|
132
|
+
fail(new Error(`app handshake refused: ${message.error}`));
|
|
133
|
+
return;
|
|
134
|
+
case "capability_invoked":
|
|
135
|
+
void handleInvocation(message.callId, message.name, message.input);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const onDisconnect = () => {
|
|
140
|
+
fail(new Error("transport disconnected during handshake"));
|
|
141
|
+
};
|
|
142
|
+
transport.connect().then(
|
|
143
|
+
() => {
|
|
144
|
+
if (settled) return;
|
|
145
|
+
unsubscribeMessage = transport.onMessage(onMessage);
|
|
146
|
+
unsubscribeDisconnect = transport.onDisconnect(onDisconnect);
|
|
147
|
+
helloTimer = setTimeout(() => {
|
|
148
|
+
fail(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));
|
|
149
|
+
}, helloTimeoutMs);
|
|
150
|
+
transport.send({ type: "app_hello", appId, protocolVersion });
|
|
151
|
+
},
|
|
152
|
+
(err) => fail(err instanceof Error ? err : new Error(String(err)))
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async function close() {
|
|
157
|
+
abortHandshake?.(new Error("app closed during handshake"));
|
|
158
|
+
unsubscribeMessage?.();
|
|
159
|
+
unsubscribeDisconnect?.();
|
|
160
|
+
unsubscribeMessage = null;
|
|
161
|
+
unsubscribeDisconnect = null;
|
|
162
|
+
session = null;
|
|
163
|
+
await transport.disconnect();
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
registerCapability,
|
|
167
|
+
deregisterCapability,
|
|
168
|
+
get capabilities() {
|
|
169
|
+
return Array.from(registry.values()).map(toWire);
|
|
170
|
+
},
|
|
171
|
+
get session() {
|
|
172
|
+
return session;
|
|
173
|
+
},
|
|
174
|
+
connect,
|
|
175
|
+
close
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function defineAppCapability(args) {
|
|
179
|
+
const inputSchema = z.toJSONSchema(args.input, {
|
|
180
|
+
target: "draft-07",
|
|
181
|
+
io: "input"
|
|
182
|
+
});
|
|
183
|
+
const outputSchema = args.output ? z.toJSONSchema(args.output, {
|
|
184
|
+
target: "draft-07",
|
|
185
|
+
io: "output"
|
|
186
|
+
}) : void 0;
|
|
187
|
+
return {
|
|
188
|
+
name: args.name,
|
|
189
|
+
displayName: args.displayName,
|
|
190
|
+
description: args.description,
|
|
191
|
+
inputSchema,
|
|
192
|
+
outputSchema,
|
|
193
|
+
side: "app",
|
|
194
|
+
scope: args.scope,
|
|
195
|
+
requiresApproval: args.requiresApproval,
|
|
196
|
+
kind: args.kind,
|
|
197
|
+
audience: args.audience,
|
|
198
|
+
promptFragment: args.promptFragment,
|
|
199
|
+
callTimeoutMs: args.callTimeoutMs,
|
|
200
|
+
render: args.render,
|
|
201
|
+
inputZod: args.input,
|
|
202
|
+
outputZod: args.output,
|
|
203
|
+
handler: args.handler
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// app-sdk/src/ws-transport.ts
|
|
208
|
+
var BEARER_SUBPROTOCOL_PREFIX = "skaile-bearer.";
|
|
209
|
+
function encodeBearerSubprotocol(token) {
|
|
210
|
+
const bytes = new TextEncoder().encode(token);
|
|
211
|
+
let binary = "";
|
|
212
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
213
|
+
const b64 = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
214
|
+
return BEARER_SUBPROTOCOL_PREFIX + b64;
|
|
215
|
+
}
|
|
216
|
+
var OPEN = 1;
|
|
217
|
+
function createWebSocketAppTransport(options) {
|
|
218
|
+
const { url, sessionToken, connectTimeoutMs = 1e4 } = options;
|
|
219
|
+
const resolved = options.WebSocketImpl ?? globalThis.WebSocket;
|
|
220
|
+
if (!resolved) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
"No WebSocket implementation available. Pass `WebSocketImpl` in a runtime without a global WebSocket."
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
const WebSocketImpl = resolved;
|
|
226
|
+
let ws = null;
|
|
227
|
+
const messageHandlers = /* @__PURE__ */ new Set();
|
|
228
|
+
const disconnectHandlers = /* @__PURE__ */ new Set();
|
|
229
|
+
function connect() {
|
|
230
|
+
return new Promise((resolve, reject) => {
|
|
231
|
+
let opened = false;
|
|
232
|
+
const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(sessionToken)]);
|
|
233
|
+
ws = socket;
|
|
234
|
+
const timer = setTimeout(() => {
|
|
235
|
+
if (opened) return;
|
|
236
|
+
socket.close(4008, "connect timeout");
|
|
237
|
+
reject(new Error(`app transport connect timed out after ${connectTimeoutMs}ms`));
|
|
238
|
+
}, connectTimeoutMs);
|
|
239
|
+
socket.addEventListener("open", () => {
|
|
240
|
+
opened = true;
|
|
241
|
+
clearTimeout(timer);
|
|
242
|
+
resolve();
|
|
243
|
+
});
|
|
244
|
+
socket.addEventListener("error", () => {
|
|
245
|
+
if (!opened) {
|
|
246
|
+
clearTimeout(timer);
|
|
247
|
+
reject(new Error("app transport connection error"));
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
socket.addEventListener("close", () => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
if (ws === socket) ws = null;
|
|
253
|
+
for (const h of disconnectHandlers) h();
|
|
254
|
+
});
|
|
255
|
+
socket.addEventListener("message", (ev) => {
|
|
256
|
+
let parsed;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(String(ev.data));
|
|
259
|
+
} catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
for (const h of messageHandlers) h(parsed);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
connect,
|
|
268
|
+
async disconnect() {
|
|
269
|
+
const socket = ws;
|
|
270
|
+
ws = null;
|
|
271
|
+
socket?.close(1e3, "client closed");
|
|
272
|
+
},
|
|
273
|
+
send(message) {
|
|
274
|
+
if (!ws || ws.readyState !== OPEN) {
|
|
275
|
+
throw new Error("app transport is not connected");
|
|
276
|
+
}
|
|
277
|
+
ws.send(JSON.stringify(message));
|
|
278
|
+
},
|
|
279
|
+
onMessage(handler) {
|
|
280
|
+
messageHandlers.add(handler);
|
|
281
|
+
return () => messageHandlers.delete(handler);
|
|
282
|
+
},
|
|
283
|
+
onDisconnect(handler) {
|
|
284
|
+
disconnectHandlers.add(handler);
|
|
285
|
+
return () => disconnectHandlers.delete(handler);
|
|
286
|
+
},
|
|
287
|
+
get connected() {
|
|
288
|
+
return ws?.readyState === OPEN;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export { DEFAULT_HELLO_TIMEOUT_MS, consoleLogger, createSkaileApp, createWebSocketAppTransport, defineAppCapability, noopLogger };
|
|
294
|
+
//# sourceMappingURL=index.js.map
|
|
295
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../app-sdk/src/logger.ts","../../app-sdk/src/app.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 * @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/**\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 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\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 completes. */\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 (`app_hello_nack`), the handshake times out, or the\n * transport disconnects mid-handshake.\n */\n connect(): Promise<AppSession>;\n /** Disconnect and tear down. Idempotent. */\n close(): Promise<void>;\n}\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 registry = new Map<string, DefinedAppCapability>();\n let session: AppSession | null = null;\n let unsubscribeMessage: (() => void) | null = null;\n let unsubscribeDisconnect: (() => void) | null = null;\n // Non-null only while a handshake is in flight; lets close() abort it\n // (clear the timer, reject the pending connect) instead of leaking a timer.\n let abortHandshake: ((err: Error) => 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 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 connect(): Promise<AppSession> {\n if (session) return Promise.resolve(session);\n if (abortHandshake) return Promise.reject(new Error(\"app connect() is already in progress\"));\n return new Promise<AppSession>((resolve, reject) => {\n let settled = false;\n let helloTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finishHandshake = () => {\n if (helloTimer) clearTimeout(helloTimer);\n abortHandshake = null;\n };\n const fail = (err: Error) => {\n if (settled) return;\n settled = true;\n finishHandshake();\n reject(err);\n };\n // Expose the abort path to close() for the whole handshake window.\n abortHandshake = fail;\n\n const onMessage = (message: AppServerMessage) => {\n switch (message.type) {\n case \"app_hello_ack\": {\n if (settled) return;\n settled = true;\n finishHandshake();\n session = { sessionId: message.sessionId, userId: message.userId };\n logger.info(\"app connection established\", { appId, sessionId: session.sessionId });\n // Register the full current surface now that we are bound.\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(session);\n return;\n }\n case \"app_hello_nack\":\n fail(new Error(`app handshake refused: ${message.error}`));\n return;\n case \"capability_invoked\":\n void handleInvocation(message.callId, message.name, message.input);\n return;\n }\n };\n\n const onDisconnect = () => {\n fail(new Error(\"transport disconnected during handshake\"));\n };\n\n transport.connect().then(\n () => {\n // close() (or an early failure) may have aborted before connect resolved.\n if (settled) return;\n unsubscribeMessage = transport.onMessage(onMessage);\n unsubscribeDisconnect = transport.onDisconnect(onDisconnect);\n helloTimer = setTimeout(() => {\n fail(new Error(`app handshake timed out after ${helloTimeoutMs}ms`));\n }, helloTimeoutMs);\n transport.send({ type: \"app_hello\", appId, protocolVersion });\n },\n (err) => fail(err instanceof Error ? err : new Error(String(err))),\n );\n });\n }\n\n async function close(): Promise<void> {\n // Abort an in-flight handshake first — rejects the pending connect and\n // clears its timer so nothing leaks past close().\n abortHandshake?.(new Error(\"app closed during handshake\"));\n unsubscribeMessage?.();\n unsubscribeDisconnect?.();\n unsubscribeMessage = null;\n unsubscribeDisconnect = null;\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 close,\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 bytes = new TextEncoder().encode(token);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\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 * 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. Presented as the WS bearer\n * subprotocol; the gateway verifies it and resolves the session/user.\n */\n sessionToken: string;\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 return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket = new WebSocketImpl(url, [encodeBearerSubprotocol(sessionToken)]);\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"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createSkaileApp` — the thin app SDK that makes an embedded app
|
|
3
|
+
* agent-controllable.
|
|
4
|
+
*
|
|
5
|
+
* An app becomes a *capability peer*, mirroring how the runner behaves toward
|
|
6
|
+
* the platform:
|
|
7
|
+
*
|
|
8
|
+
* 1. Opens an authenticated back-connection (via an {@link AppTransport}).
|
|
9
|
+
* 2. Performs the app handshake — sends `app_hello`, awaits `app_hello_ack`
|
|
10
|
+
* (binding the connection to `session + appId`) or `app_hello_nack` (refusal).
|
|
11
|
+
* 3. Registers its `app.*` capabilities via `capability_register`, stamping
|
|
12
|
+
* each with `origin:{kind:'app', appId}` and `side:'app'`. Register /
|
|
13
|
+
* deregister mid-session is supported.
|
|
14
|
+
* 4. Answers inbound `capability_invoked` events by running the local handler
|
|
15
|
+
* and replying with `capability_result` (a thrown handler rides back as
|
|
16
|
+
* `{ error }`).
|
|
17
|
+
*
|
|
18
|
+
* The approval round-trip for `requiresApproval` capabilities is handled
|
|
19
|
+
* entirely platform-side: the gateway parks the invocation for user approval
|
|
20
|
+
* and only forwards `capability_invoked` to the app *after* approval, so the
|
|
21
|
+
* app just marks the capability and answers when asked.
|
|
22
|
+
*
|
|
23
|
+
* @category App SDK
|
|
24
|
+
* @since 3.4.0
|
|
25
|
+
*/
|
|
26
|
+
import type { Capability } from "@skaile/workspaces/types";
|
|
27
|
+
import type { DefinedAppCapability } from "./define-app-capability.js";
|
|
28
|
+
import { type AppLogger } from "./logger.js";
|
|
29
|
+
import type { AppTransport } from "./transport.js";
|
|
30
|
+
/** Default timeout waiting for the gateway's `app_hello_ack`. */
|
|
31
|
+
export declare const DEFAULT_HELLO_TIMEOUT_MS = 10000;
|
|
32
|
+
/**
|
|
33
|
+
* Options for {@link createSkaileApp}.
|
|
34
|
+
*
|
|
35
|
+
* @category App SDK
|
|
36
|
+
* @since 3.4.0
|
|
37
|
+
*/
|
|
38
|
+
export interface SkaileAppOptions {
|
|
39
|
+
/** The embedded app's stable id. Every registered capability is bound to it. */
|
|
40
|
+
appId: string;
|
|
41
|
+
/**
|
|
42
|
+
* The app back-connection. Inject {@link createWebSocketAppTransport} in
|
|
43
|
+
* production or a fake in tests. The transport carries the injected session
|
|
44
|
+
* identity as its connection credential.
|
|
45
|
+
*/
|
|
46
|
+
transport: AppTransport;
|
|
47
|
+
/** Protocol version the SDK announces in `app_hello`. Defaults to the SDK's. */
|
|
48
|
+
protocolVersion?: string;
|
|
49
|
+
/** Timeout waiting for `app_hello_ack`. Defaults to {@link DEFAULT_HELLO_TIMEOUT_MS}. */
|
|
50
|
+
helloTimeoutMs?: number;
|
|
51
|
+
/** Logger for the SDK and (by default) capability handlers. Defaults to {@link noopLogger}. */
|
|
52
|
+
logger?: AppLogger;
|
|
53
|
+
}
|
|
54
|
+
/** Result of a successful handshake — the bound session context. */
|
|
55
|
+
export interface AppSession {
|
|
56
|
+
sessionId: string;
|
|
57
|
+
userId?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A connected (or connectable) Skaile app. Register capabilities before or
|
|
61
|
+
* after {@link connect}; post-connect changes are pushed to the gateway
|
|
62
|
+
* immediately.
|
|
63
|
+
*
|
|
64
|
+
* @category App SDK
|
|
65
|
+
* @since 3.4.0
|
|
66
|
+
*/
|
|
67
|
+
export interface SkaileApp {
|
|
68
|
+
/**
|
|
69
|
+
* Add (or replace, by name) an app capability. Registering the same name
|
|
70
|
+
* again replaces it — mirrors the registry's replace-by-name semantics. When
|
|
71
|
+
* already connected, a `capability_register` is sent immediately.
|
|
72
|
+
*/
|
|
73
|
+
registerCapability(cap: DefinedAppCapability): void;
|
|
74
|
+
/**
|
|
75
|
+
* Remove a capability by name. No-op when unknown. When connected, a
|
|
76
|
+
* `capability_deregister` is sent immediately so the tool disappears from the
|
|
77
|
+
* agent's catalog.
|
|
78
|
+
*/
|
|
79
|
+
deregisterCapability(name: string): void;
|
|
80
|
+
/** Wire-format snapshot of the currently registered capabilities (origin stamped). */
|
|
81
|
+
readonly capabilities: readonly Capability[];
|
|
82
|
+
/** The bound session once connected; `null` before the handshake completes. */
|
|
83
|
+
readonly session: AppSession | null;
|
|
84
|
+
/**
|
|
85
|
+
* Open the connection, perform the handshake, and register the current
|
|
86
|
+
* capabilities. Resolves with the bound {@link AppSession}. Rejects if the
|
|
87
|
+
* gateway refuses (`app_hello_nack`), the handshake times out, or the
|
|
88
|
+
* transport disconnects mid-handshake.
|
|
89
|
+
*/
|
|
90
|
+
connect(): Promise<AppSession>;
|
|
91
|
+
/** Disconnect and tear down. Idempotent. */
|
|
92
|
+
close(): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a {@link SkaileApp}. See the module doc for the peer lifecycle.
|
|
96
|
+
*
|
|
97
|
+
* @category App SDK
|
|
98
|
+
* @since 3.4.0
|
|
99
|
+
*/
|
|
100
|
+
export declare function createSkaileApp(options: SkaileAppOptions): SkaileApp;
|
|
101
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../app-sdk/src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,EAAE,YAAY,CAAC;IACxB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,kBAAkB,CAAC,GAAG,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACpD;;;;OAIG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,sFAAsF;IACtF,QAAQ,CAAC,YAAY,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7C,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC;;;;;OAKG;IACH,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAoKpE"}
|