@puredesktop/create-app 2.1.0 → 2.1.2

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.
@@ -0,0 +1,58 @@
1
+ # App Agent
2
+
3
+ Every generated app has an app-scoped assistant that runs in the shell agent
4
+ drawer. The app does not render its own agent chat UI by default.
5
+
6
+ ## Source Files
7
+
8
+ - `agents.md`: app-scoped assistant prompt.
9
+ - `plugin.json` `app.agents.tools`: tool schemas exposed to the agent.
10
+ - `src/agents/*`: generated app-owned tool catalog and handlers when tools
11
+ exist.
12
+ - `src/hooks/useAppAgentTools.ts`: generated runtime registration hook when
13
+ tools exist.
14
+
15
+ ## How The Agent Uses App Tools
16
+
17
+ 1. The shell reads `plugin.json` and includes declared app tools in the app
18
+ agent's available tools.
19
+ 2. The app iframe loads and registers runtime handlers with
20
+ `usePlatformAgentTools`.
21
+ 3. The agent calls a declared tool during a shell-managed session.
22
+ 4. The shell routes the invocation to the app iframe.
23
+ 5. The app handler returns compact text or JSON.
24
+ 6. The bridge helper completes the tool call back to the shell.
25
+ 7. The shell resumes the agent session.
26
+
27
+ The app owns the handler implementation. The shell owns session state,
28
+ approvals, routing, and completion.
29
+
30
+ When `plugin.json` contains `app.agents.tools`, the create-app generator writes
31
+ the initial catalog, handler map, and registration hook. The generated handlers
32
+ return explicit not-implemented tool errors until the app replaces them with real
33
+ domain behavior.
34
+
35
+ ## agents.md
36
+
37
+ Keep `agents.md` grounded in the app's real UI and data. Mention the app's work
38
+ objects, read-first workflow, write safety rules, and expected concise output.
39
+
40
+ Example:
41
+
42
+ ```md
43
+ # {{APP_TITLE}} Agent
44
+
45
+ You are the app-scoped assistant for {{APP_TITLE}}.
46
+
47
+ Help the user understand, review, and act on the work inside this app. Stay
48
+ grounded in the app UI and data. Prefer concise answers, concrete next actions,
49
+ and safe tool use.
50
+ ```
51
+
52
+ ## Approval Policy
53
+
54
+ Set `requiresApproval` on each manifest tool. Read-only inspection tools usually
55
+ set `false`. Destructive, external-effect, or broad write tools set `true`.
56
+
57
+ The shell drawer displays approvals and resumes the same session after the user
58
+ approves or rejects.
@@ -0,0 +1,60 @@
1
+ # App Lifecycle
2
+
3
+ A PureDesktop app is loaded into an iframe by the shell. The shell discovers the
4
+ app from `plugin.json`, opens a workspace tab, loads the entrypoint URL or built
5
+ static bundle, then establishes the bridge handshake with the iframe.
6
+
7
+ ## Registration To Render
8
+
9
+ 1. The shell scans installed app folders for `plugin.json`.
10
+ 2. The catalog creates one app entry from `plugin.json` `app.slug`.
11
+ 3. Opening the app creates a workspace tab.
12
+ 4. The tab renders the app entrypoint in an iframe.
13
+ 5. The iframe starts React from `src/main.tsx`.
14
+ 6. `src/App.tsx` calls `usePlatformBridge()`.
15
+ 7. The bridge client sends a handshake to the shell parent window.
16
+ 8. The shell replies with `ready` metadata, including `pluginId`, `appSlug`,
17
+ allowed method names, theme CSS, and optional viewport resource.
18
+ 9. The app boot hook loads app settings and other startup data.
19
+ 10. The app renders inside `AppFrame`.
20
+
21
+ ## App.tsx Contract
22
+
23
+ `src/App.tsx` is the orchestration boundary. Keep it thin:
24
+
25
+ - call `usePlatformBridge()`;
26
+ - allow standalone browser dev mode when needed;
27
+ - call the app boot hook after the bridge is ready;
28
+ - render loading and error states inside `AppFrame`;
29
+ - render the app shell inside `AppFrame` after boot succeeds.
30
+
31
+ The template already follows this shape.
32
+
33
+ ## Viewport Resources
34
+
35
+ When the shell opens a file or app resource into a tab, the bridge ready metadata
36
+ can include:
37
+
38
+ ```ts
39
+ {
40
+ viewport: {
41
+ tabId: string
42
+ resource: { path: string; name: string; kind: string } | null
43
+ }
44
+ }
45
+ ```
46
+
47
+ File-centric apps use `usePlatformViewportResource(ready, meta)` from the bridge
48
+ React helpers. This handles cold opens and warm `resource.open` events without
49
+ duplicating event wiring in screens.
50
+
51
+ ## Standalone Dev
52
+
53
+ The template supports browser-only development with:
54
+
55
+ ```ts
56
+ window.parent === window
57
+ ```
58
+
59
+ Standalone mode is for UI iteration. Production app behavior runs inside the
60
+ shell iframe and uses shell bridge permissions.
@@ -0,0 +1,84 @@
1
+ # Platform Bridge Helpers
2
+
3
+ The bridge is the app iframe's RPC channel to the PureDesktop shell.
4
+
5
+ The generated app runs in an iframe. It cannot call shell internals directly.
6
+ Instead, app code imports helpers from `src/bridge/platformBridge.ts`. That
7
+ local bridge module wraps the package bridge client from
8
+ `@puredesktop/puredesktop-ui-bridge`.
9
+
10
+ ## Runtime Boundary
11
+
12
+ ```text
13
+ App iframe -> bridge helper -> postMessage -> shell renderer -> shell handler
14
+ ```
15
+
16
+ The shell validates the app origin, method name, and `plugin.json` permissions
17
+ before running a handler.
18
+
19
+ ## Ready Handshake
20
+
21
+ `src/App.tsx` uses `usePlatformBridge()` to wait for the shell:
22
+
23
+ ```ts
24
+ const { ready, error, meta } = usePlatformBridge()
25
+ ```
26
+
27
+ The shell ready payload includes:
28
+
29
+ - `pluginId`: plugin package identity.
30
+ - `appSlug`: app API scope.
31
+ - `methods`: bridge methods available in this shell.
32
+ - `theme`: injected theme CSS.
33
+ - `viewport`: optional tab resource opened into the app.
34
+
35
+ Call bridge helpers after `ready` is true. The template boot hook follows that
36
+ pattern.
37
+
38
+ ## Local Bridge Module
39
+
40
+ `src/bridge/platformBridge.ts` is the app's bridge boundary. Components and
41
+ feature code import from that local file:
42
+
43
+ ```ts
44
+ import { fetchAppSettings, networkFetch } from '../bridge/platformBridge'
45
+ ```
46
+
47
+ Keep request-shape knowledge in this bridge folder. Component code calls
48
+ domain-named helpers.
49
+
50
+ `platformBridge.ts` is both the exported app bridge surface and the place for
51
+ app-owned bridge wrappers. Package bridge helpers are imported as local bindings
52
+ first, then re-exported for app code. App-specific wrappers in this file should
53
+ call those local bindings.
54
+
55
+ ## Helper Domains
56
+
57
+ - [Dialog](./dialog.md)
58
+ - [Filesystem](./fs.md)
59
+ - [Network](./network.md)
60
+ - [Settings](./settings.md)
61
+ - [Storage](./storage.md)
62
+
63
+ ## Permissions
64
+
65
+ Bridge calls are gated by top-level `plugin.json` `permissions`.
66
+
67
+ | Permission | Helper domains |
68
+ | --- | --- |
69
+ | `settings` | settings and preferences |
70
+ | `filesystem` | filesystem, dialogs, storage, file routing |
71
+ | `network` | network requests and OAuth-style flows |
72
+ | `render` | print and render helpers |
73
+ | `assets` | asset library helpers |
74
+ | `agents` | agent sessions, agent tools, credentials |
75
+
76
+ ## Events
77
+
78
+ The bridge also delivers shell events into the iframe. Common app events include
79
+ theme changes, viewport resource opens, render progress, filesystem watch
80
+ changes, agent run events, and app tool invocations.
81
+
82
+ Use the package React hooks when they exist. For example,
83
+ `usePlatformViewportResource(ready, meta)` handles file opens without manual
84
+ event wiring.
@@ -0,0 +1,56 @@
1
+ # Dialog
2
+
3
+ Permission: `filesystem`
4
+
5
+ Dialog helpers open shell-native file and folder pickers.
6
+
7
+ Import from the generated app bridge:
8
+
9
+ ```ts
10
+ import {
11
+ openPlatformFileDialog,
12
+ openPlatformFolderDialog,
13
+ openPlatformImageDialog,
14
+ savePlatformFolderDialog,
15
+ } from '../../src/bridge/platformBridge'
16
+ ```
17
+
18
+ ## openPlatformFolderDialog
19
+
20
+ Opens a folder picker.
21
+
22
+ ```ts
23
+ const folderPath = await openPlatformFolderDialog()
24
+ ```
25
+
26
+ Returns the selected absolute folder path, or `null` when cancelled.
27
+
28
+ ## savePlatformFolderDialog
29
+
30
+ Opens a folder picker configured for choosing a parent directory.
31
+
32
+ ```ts
33
+ const parentPath = await savePlatformFolderDialog()
34
+ ```
35
+
36
+ Returns the selected absolute folder path, or `null` when cancelled.
37
+
38
+ ## openPlatformImageDialog
39
+
40
+ Opens an image file picker.
41
+
42
+ ```ts
43
+ const imagePath = await openPlatformImageDialog()
44
+ ```
45
+
46
+ Returns the selected absolute image path, or `null` when cancelled.
47
+
48
+ ## openPlatformFileDialog
49
+
50
+ Opens a text/document file picker.
51
+
52
+ ```ts
53
+ const filePath = await openPlatformFileDialog()
54
+ ```
55
+
56
+ Returns the selected absolute file path, or `null` when cancelled.
@@ -0,0 +1,95 @@
1
+ # Filesystem
2
+
3
+ Permission: `filesystem`
4
+
5
+ Filesystem helpers read, write, list, rename, delete, and preview files through
6
+ the shell.
7
+
8
+ Import from the generated app bridge:
9
+
10
+ ```ts
11
+ import {
12
+ createPlatformFolder,
13
+ deletePlatformFile,
14
+ listPlatformFiles,
15
+ readPlatformTextFile,
16
+ renamePlatformFile,
17
+ writePlatformTextFile,
18
+ } from '../../src/bridge/platformBridge'
19
+ ```
20
+
21
+ ## listPlatformFiles
22
+
23
+ Lists a folder.
24
+
25
+ ```ts
26
+ const listing = await listPlatformFiles('/absolute/folder')
27
+ ```
28
+
29
+ Returns:
30
+
31
+ - `rootPath`: listed folder path.
32
+ - `parentPath`: parent folder path or `null`.
33
+ - `entries`: file and folder entries with `path`, `name`, `kind`, size,
34
+ modified time, MIME type, extension, and directory flag.
35
+
36
+ ## readPlatformTextFile
37
+
38
+ Reads a text file.
39
+
40
+ ```ts
41
+ const content = await readPlatformTextFile('/absolute/file.md')
42
+ ```
43
+
44
+ Returns the file contents as a string.
45
+
46
+ ## writePlatformTextFile
47
+
48
+ Writes a text file.
49
+
50
+ ```ts
51
+ await writePlatformTextFile('/absolute/file.md', '# Notes\n')
52
+ ```
53
+
54
+ Returns `{ ok: true }`.
55
+
56
+ ## createPlatformFolder
57
+
58
+ Creates a folder under a parent directory.
59
+
60
+ ```ts
61
+ const folder = await createPlatformFolder('/absolute/parent', 'Project')
62
+ ```
63
+
64
+ Returns `{ path }` for the created folder.
65
+
66
+ ## renamePlatformFile
67
+
68
+ Renames a file or folder within its current parent directory.
69
+
70
+ ```ts
71
+ const renamed = await renamePlatformFile('/absolute/file.md', 'renamed.md')
72
+ ```
73
+
74
+ Returns `{ path }` for the renamed path.
75
+
76
+ ## deletePlatformFile
77
+
78
+ Deletes a file or folder.
79
+
80
+ ```ts
81
+ await deletePlatformFile('/absolute/file.md')
82
+ await deletePlatformFile('/absolute/folder', true)
83
+ ```
84
+
85
+ Pass `true` for recursive folder deletion. Returns `{ ok: true }`.
86
+
87
+ ## Preview And Binary Helpers
88
+
89
+ The same domain also exports:
90
+
91
+ - `readPlatformFilePreview(path, maxBytes?)`
92
+ - `readPlatformFilePreviewUrl(path)`
93
+ - `readPlatformFileBinary(path, maxBytes?)`
94
+ - `readPlatformFileBinaryDataUrl(path, maxBytes?)`
95
+ - `writePlatformFileBinary(path, base64)`
@@ -0,0 +1,38 @@
1
+ # Network
2
+
3
+ Permission: `network`
4
+
5
+ Use `networkFetch` for HTTP(S) requests through the shell.
6
+
7
+ Import from the generated app bridge:
8
+
9
+ ```ts
10
+ import { networkFetch } from '../../src/bridge/platformBridge'
11
+ ```
12
+
13
+ Example:
14
+
15
+ ```ts
16
+ const response = await networkFetch({
17
+ url: 'https://example.com/api/items',
18
+ method: 'GET',
19
+ })
20
+
21
+ if (!response.ok) {
22
+ throw new Error(`Request failed with ${response.status}`)
23
+ }
24
+ ```
25
+
26
+ Request fields:
27
+
28
+ - `url`: HTTP or HTTPS URL.
29
+ - `method`: optional, defaults to `GET`.
30
+ - `headers`: optional string map.
31
+ - `body`: optional string.
32
+
33
+ Response fields:
34
+
35
+ - `status`: HTTP status code.
36
+ - `ok`: `true` for 2xx responses.
37
+ - `headers`: response headers as a string map.
38
+ - `body`: response body as text.
@@ -0,0 +1,63 @@
1
+ # Settings
2
+
3
+ Permission: `settings`
4
+
5
+ Settings helpers read and update user preferences through the shell. App
6
+ settings are stored inside user preferences under the current app's slug.
7
+
8
+ Import from the generated app bridge:
9
+
10
+ ```ts
11
+ import {
12
+ getPlatformAppSettings,
13
+ getPlatformPreferences,
14
+ patchPlatformPreferences,
15
+ updatePlatformAppSettings,
16
+ } from '../../src/bridge/platformBridge'
17
+ ```
18
+
19
+ ## getPlatformAppSettings
20
+
21
+ Reads settings for the current app slug.
22
+
23
+ ```ts
24
+ const settings = await getPlatformAppSettings(APP_SLUG)
25
+ ```
26
+
27
+ The shell allows plugin frames to access only their own app settings.
28
+
29
+ ## updatePlatformAppSettings
30
+
31
+ Patches settings for the current app slug.
32
+
33
+ ```ts
34
+ const next = await updatePlatformAppSettings({
35
+ appSlug: APP_SLUG,
36
+ patch: { workspacePath: '/absolute/folder' },
37
+ })
38
+ ```
39
+
40
+ Returns the merged app settings object.
41
+
42
+ ## getPlatformPreferences
43
+
44
+ Reads shell user preferences.
45
+
46
+ ```ts
47
+ const prefs = await getPlatformPreferences()
48
+ ```
49
+
50
+ Common fields include the working directory, theme, app settings, user profile,
51
+ and agent defaults.
52
+
53
+ ## patchPlatformPreferences
54
+
55
+ Patches shell user preferences.
56
+
57
+ ```ts
58
+ const next = await patchPlatformPreferences({
59
+ theme: 'dark',
60
+ })
61
+ ```
62
+
63
+ Returns the merged user preferences object.
@@ -0,0 +1,48 @@
1
+ # Storage
2
+
3
+ Permission: `filesystem`
4
+
5
+ Storage helpers read and write JSON files in the shell-managed PureDesktop data
6
+ folder. These helpers use the same bridge path as the exposed
7
+ `storage.readJson` and `storage.writeJson` methods.
8
+
9
+ Import from the generated app bridge:
10
+
11
+ ```ts
12
+ import {
13
+ readPlatformStorageJson,
14
+ writePlatformStorageJson,
15
+ } from '../../src/bridge/platformBridge'
16
+ ```
17
+
18
+ ## readPlatformStorageJson
19
+
20
+ Reads an app JSON store file.
21
+
22
+ ```ts
23
+ const result = await readPlatformStorageJson({
24
+ appSlug: APP_SLUG,
25
+ fileName: 'items.json',
26
+ })
27
+ ```
28
+
29
+ Returns:
30
+
31
+ - `path`: resolved storage path.
32
+ - `value`: parsed JSON value, or `null` when the file is absent.
33
+
34
+ ## writePlatformStorageJson
35
+
36
+ Writes an app JSON store file.
37
+
38
+ ```ts
39
+ await writePlatformStorageJson({
40
+ appSlug: APP_SLUG,
41
+ fileName: 'items.json',
42
+ value: { items: [] },
43
+ })
44
+ ```
45
+
46
+ Returns `{ path, ok: true }`.
47
+
48
+ Storage file names must be file names ending with `.json`.
@@ -0,0 +1,108 @@
1
+ # Plugin Manifest
2
+
3
+ `plugin.json` is the shell contract for discovery, permissions, routing, and app
4
+ agent tools.
5
+
6
+ ## Required Shape
7
+
8
+ ```json
9
+ {
10
+ "schemaVersion": 1,
11
+ "id": "{{PLUGIN_ID}}",
12
+ "name": "{{APP_TITLE}}",
13
+ "permissions": ["network", "settings", "agents"],
14
+ "entrypoint": {
15
+ "kind": "dev-url",
16
+ "url": "http://localhost:{{APP_PORT}}"
17
+ },
18
+ "app": {
19
+ "id": "plugin.{{APP_SLUG}}",
20
+ "slug": "{{APP_SLUG}}",
21
+ "name": "{{APP_TITLE}}",
22
+ "navigationLabel": "{{APP_TITLE}}",
23
+ "productName": "pure.{{APP_SLUG}}",
24
+ "kind": "{{APP_SLUG}}",
25
+ "description": "{{APP_TITLE}} - built with PureDesktop.",
26
+ "usePureDesktopAiPanel": true
27
+ }
28
+ }
29
+ ```
30
+
31
+ `app.slug` is the API scope for settings, storage, app agent sessions, and tab
32
+ routing. `src/constants.ts` `APP_SLUG` must match `plugin.json` `app.slug`.
33
+
34
+ ## Entrypoint
35
+
36
+ Development apps use:
37
+
38
+ ```json
39
+ {
40
+ "entrypoint": {
41
+ "kind": "dev-url",
42
+ "url": "http://localhost:{{APP_PORT}}"
43
+ }
44
+ }
45
+ ```
46
+
47
+ Built apps use a static bundle registered from `dist/`. The validator checks the
48
+ build output before registration.
49
+
50
+ ## Permissions
51
+
52
+ Declare every shell capability used by bridge helpers:
53
+
54
+ | Permission | Enables |
55
+ | --- | --- |
56
+ | `settings` | `settings.*`, app settings, user preferences |
57
+ | `filesystem` | `fs.*`, `dialog.*`, `storage.*`, file open routing |
58
+ | `network` | `network.fetch`, hosted entrypoints, OAuth helpers |
59
+ | `render` | document render and print helpers |
60
+ | `assets` | asset library helpers |
61
+ | `agents` | app agent sessions and runtime tool handlers |
62
+
63
+ ## App Agent Tools
64
+
65
+ Declare app-owned tools under `app.agents.tools`:
66
+
67
+ ```json
68
+ {
69
+ "app": {
70
+ "agents": {
71
+ "tools": [
72
+ {
73
+ "name": "listItems",
74
+ "description": "List the current items with ids, titles, status, and next action.",
75
+ "inputSchema": {
76
+ "type": "object",
77
+ "properties": {
78
+ "status": {
79
+ "type": "string",
80
+ "description": "Optional status filter."
81
+ }
82
+ },
83
+ "additionalProperties": false
84
+ },
85
+ "requiresApproval": false
86
+ }
87
+ ]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ The manifest declaration exposes the tool to the shell agent. Runtime handler
94
+ registration in the iframe executes the tool.
95
+
96
+ ## Validation
97
+
98
+ Run:
99
+
100
+ ```bash
101
+ npm run typecheck
102
+ npm run build
103
+ npm run puredesktop:check
104
+ ```
105
+
106
+ The validator checks manifest identity, required scripts and dependencies,
107
+ `agents.md`, `app.usePureDesktopAiPanel`, permissions, built output, and declared
108
+ agent tool presence in source and dist.