@hearthscale/app 0.0.0-stage → 0.2.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/LICENSE +25 -0
- package/README.md +109 -2
- package/dist/index.d.ts +5318 -0
- package/dist/view.d.ts +66 -0
- package/dist/view.js +20 -0
- package/package.json +45 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon Wagenfuehrer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
The fonts in `dist/fonts` are licensed under the SIL Open Font License 1.1
|
|
24
|
+
(`dist/fonts/OFL.txt`). The Remix Icon font in `dist/remixicon` is licensed
|
|
25
|
+
under the Apache License 2.0 (`dist/remixicon/License`).
|
package/README.md
CHANGED
|
@@ -1,3 +1,110 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hearthscale/app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The types an app imports: its manifest, `app.json` (`Manifest`), what
|
|
4
|
+
`backend.js` exports (`Backend`), what its
|
|
5
|
+
tool bodies receive (`AppContext`, `ToolRun`), and the slots a view
|
|
6
|
+
reserves for the shell's chat, composer, cards, live terminal and the
|
|
7
|
+
tab's web page (`HostSlot`). The package carries no runtime for a
|
|
8
|
+
backend: `hearthscale dev` and `hearthscale pack` bundle an import of
|
|
9
|
+
`@hearthscale/app` as an empty module, so a backend ships nothing of the
|
|
10
|
+
platform and imports only its types. `@hearthscale/app/view` is the one
|
|
11
|
+
runtime part, which a view bundles.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install --save-dev @hearthscale/app
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Type-check a backend with `"lib": ["ES2022", "DOM"]`: `AppContext.signal`
|
|
18
|
+
is an `AbortSignal` and `models.complete` answers an `AsyncIterable`. That
|
|
19
|
+
library also declares `fetch` and `URL`, which the backend's engine does not
|
|
20
|
+
have; a backend reaches the network with `ctx.net`. The manifest's schema is
|
|
21
|
+
`https://hearthscale.com/app.schema.json`.
|
|
22
|
+
|
|
23
|
+
A view draws with the UI kit, `@hearthscale/app-ui` beside this package:
|
|
24
|
+
the tokens, the `hs-*` classes, React parts and the Remix Icon font the host
|
|
25
|
+
loads into every view.
|
|
26
|
+
|
|
27
|
+
A view calls the methods every view may call (`ViewHostMethods`), the
|
|
28
|
+
`hearthscale/*` extensions and `openai/files/open` (`ViewExtensionMethods`)
|
|
29
|
+
as requests of its MCP Apps `App`, and hears the host's notifications
|
|
30
|
+
(`HostSlotNotification`, `ViewExtensionNotification`) on
|
|
31
|
+
`app.fallbackNotificationHandler`. `ViewHostContext` types the keys the host
|
|
32
|
+
adds to the host context, and `ContentBlockMeta`, `ModelContextAnswer`,
|
|
33
|
+
`MessageOptions`, `ResourceReadOptions`, `ResourceVersion` and
|
|
34
|
+
`ResourcePath` the `_meta` of the standard messages. `hostCall` of
|
|
35
|
+
`@hearthscale/app/view` gives one typed call of every request a view sends
|
|
36
|
+
beyond MCP Apps, the host methods and the extensions alike (`HostCall`,
|
|
37
|
+
over `ViewMethods`), and of the MCP Apps requests that take Hearthscale's
|
|
38
|
+
own fields (`ViewStandardMethods`; `ViewCall` covers both):
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { App } from '@modelcontextprotocol/ext-apps';
|
|
42
|
+
import { hostCall } from '@hearthscale/app/view';
|
|
43
|
+
|
|
44
|
+
const app = new App({ name: 'Inbox', version: '1.0.0' }, {});
|
|
45
|
+
const call = hostCall(app);
|
|
46
|
+
const { sessions } = await call('hearthscale/sessions/list');
|
|
47
|
+
await call('hearthscale/ui/set-tab', { title: 'Inbox' });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A view keeps a JSON value for its next load with
|
|
51
|
+
`hearthscale/ui/set-widget-state` (`{ state }`, at most 65,536 UTF-16 code units as JSON text) and
|
|
52
|
+
reads it back from the host context as `hearthscale/widgetState`, null
|
|
53
|
+
before the first save. The host loads a view in a tab again when the tab
|
|
54
|
+
comes back after the window discarded its surface, and after a restart.
|
|
55
|
+
A view that fills a tab names it with `hearthscale/ui/set-tab`
|
|
56
|
+
(`{ title?, icon? }`, the icon a picture as a `data:` address); null gives
|
|
57
|
+
back the surface's own title or mark, and the tab keeps the name through a
|
|
58
|
+
restart.
|
|
59
|
+
|
|
60
|
+
A view whose app names `models` in `uses` asks the model with MCP's
|
|
61
|
+
`sampling/createMessage` (text only, on the app's chat card).
|
|
62
|
+
`hearthscale/ui/select-files` (`{ accept?, multiple? }`, on a click) answers
|
|
63
|
+
the files the person picked, which the view reads with `resources/read`;
|
|
64
|
+
`hearthscale/ui/upload-file` (`{ name, mediaType, blob }`, `blob` a `Blob`)
|
|
65
|
+
keeps its bytes as an asset the view reads back as
|
|
66
|
+
`hearthscale-asset://<asset>`, or streamed and by byte range at the address
|
|
67
|
+
`hearthscale/assetBytes` in the host context names, followed by the asset's
|
|
68
|
+
id, as a `fetch` or as the source of a picture, a sound or a video.
|
|
69
|
+
`ui/download-file` saves where the person picks on a click; an embedded
|
|
70
|
+
resource's `blob` may be a `Blob`, which streams into the file, beside the
|
|
71
|
+
text and the base64 of MCP Apps, which hold at most 64 MiB.
|
|
72
|
+
|
|
73
|
+
A view that fills a tab has a web page of its tab: the shell loads it on
|
|
74
|
+
the app's own partition, where it reaches only the hosts of
|
|
75
|
+
`environment.network`, once the view asks for an address with
|
|
76
|
+
`hearthscale/web/navigate`. The page lies over the view's `web` slot, and
|
|
77
|
+
under the view while the view covers it (`hearthscale/ui/cover`), so a
|
|
78
|
+
menu the view opens draws over the page and the page shows through where
|
|
79
|
+
the view draws nothing. The same call lays the chat and the composer of the
|
|
80
|
+
view's slots under it; the terminal and the cards stay above. The view drives the page with the other
|
|
81
|
+
`hearthscale/web/*` methods and hears its state as `hearthscale/web/page`
|
|
82
|
+
(`WebPageState`). A link the page opens in a new window opens a tab of the
|
|
83
|
+
same surface, and a popup with a size of its own opens as a window that
|
|
84
|
+
keeps its opener. The keys the view names with `hearthscale/web/keys` reach
|
|
85
|
+
the view as `hearthscale/web/key` while the page has the keyboard. The page
|
|
86
|
+
keeps its address through a restart, and the app's tools drive it through
|
|
87
|
+
`surface.command`.
|
|
88
|
+
|
|
89
|
+
An app that names `peers` in `uses` talks to itself on other computers in
|
|
90
|
+
rooms. A view opens a room with `hearthscale/peers/create` and shows the
|
|
91
|
+
person its ticket to pass on; `hearthscale/peers/join` asks the person for
|
|
92
|
+
a ticket in a sheet the shell draws, so the view never names one. Both
|
|
93
|
+
need a click. The view and the backend (`ctx.peers`) then send messages,
|
|
94
|
+
JSON of at most 64 KiB, to the room's other members, hear
|
|
95
|
+
theirs (`hearthscale/peers/message`, `ctx.peers.on('message')`) and leave.
|
|
96
|
+
A room carries only these messages: no file, process or credential of
|
|
97
|
+
either platform. It ends for everyone when the computer that opened it
|
|
98
|
+
leaves. A member keeps its place and joins again on its own after a drop
|
|
99
|
+
or a restart; no message is kept.
|
|
100
|
+
|
|
101
|
+
## Traps
|
|
102
|
+
|
|
103
|
+
- The channel carries JSON only. Bytes cross as base64 inside the host;
|
|
104
|
+
the context's `assets` and `net` methods hide that, but a tool body
|
|
105
|
+
that returns a `Uint8Array` returns nothing useful: publish it as an
|
|
106
|
+
asset and answer the id.
|
|
107
|
+
- `App.request` checks a result against the schema it is given, and drops
|
|
108
|
+
every field the schema does not name unless the schema keeps them.
|
|
109
|
+
`McpUiMessageResultSchema` keeps every field, so an extension's result
|
|
110
|
+
arrives whole.
|