@akanjs/cli 3.0.0-beta.27 → 3.0.0-beta.28

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.
Files changed (61) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-h19atbr3.js → CodeAgent-7acv4ah8.js} +2 -2
  3. package/{CodeTui-46zygq8p.js → CodeTui-9tkh34mj.js} +1 -1
  4. package/{agent.command-rp505zxa.js → agent.command-q9bmqrag.js} +5 -5
  5. package/akanCode.js +2 -2
  6. package/{application.command-1p0cpt6y.js → application.command-xpnkyx4z.js} +70 -8
  7. package/{applicationBuildRunner-r6b9rkb8.js → applicationBuildRunner-w4917tje.js} +67 -22
  8. package/buildBatch.proc.js +3 -3
  9. package/{cloud.command-ps9dabbg.js → cloud.command-7yd624qd.js} +9 -9
  10. package/cloud.script-25vzkz7b.js +20 -0
  11. package/{code.command-78qw3q3f.js → code.command-fpx4gzmy.js} +6 -6
  12. package/commandManifest.json +1 -1
  13. package/{context.command-x5f7t3gk.js → context.command-snn1k5q3.js} +13 -13
  14. package/context.runner-nzaf8c98.js +21 -0
  15. package/{guideline.command-d5qmxx29.js → guideline.command-ene55fkr.js} +2 -2
  16. package/guidelines/conventions/conventions.instruction.md +27 -7
  17. package/guidelines/runtimeRule/runtimeRule.instruction.md +107 -1
  18. package/incrementalBuilder.proc.js +2 -2
  19. package/{index-118vq9dh.js → index-0ej0sr6m.js} +225 -46
  20. package/{index-hzy4pe24.js → index-0pqzrbpr.js} +2 -2
  21. package/{index-6dt6zft6.js → index-20r2g1t3.js} +4 -4
  22. package/{index-xfgdmm5d.js → index-2ctqegdm.js} +2 -2
  23. package/{index-d65fzxvd.js → index-2s2e5qnd.js} +1 -1
  24. package/{index-t02g6dpn.js → index-2z30v4k5.js} +3 -3
  25. package/{index-vxqe24mp.js → index-52n8xr63.js} +1 -1
  26. package/{index-tg09x14f.js → index-9j0kykc6.js} +5 -5
  27. package/{index-58cyna2s.js → index-b68zfsvz.js} +1 -1
  28. package/{index-2zcm7fjd.js → index-ewbek95g.js} +3 -3
  29. package/{index-bp9b1g2r.js → index-fy42hyb9.js} +7 -3
  30. package/{index-zazvn45w.js → index-genegfnt.js} +4 -4
  31. package/{index-da72pfdv.js → index-h9ed99ax.js} +7 -7
  32. package/{index-per2kqy9.js → index-k7809wmg.js} +2 -2
  33. package/{index-j9z8vg2n.js → index-n9704w6y.js} +4 -4
  34. package/{index-860zjnr1.js → index-nbg9bfnr.js} +1 -1
  35. package/{index-5mm3g24t.js → index-qtz88kh0.js} +2 -2
  36. package/{index-0n9q1f23.js → index-r8gp8zk3.js} +231 -98
  37. package/{index-ggfhz9ab.js → index-rbeahw6j.js} +3 -3
  38. package/{index-56xwsgyn.js → index-rrt9pnb4.js} +2 -2
  39. package/{index-v4hnkqz9.js → index-ss828r32.js} +1 -1
  40. package/index-tfe3711h.js +947 -0
  41. package/{index-5kjsr2pg.js → index-ttn00pr3.js} +38 -3
  42. package/{index-w5pet0nq.js → index-vt64c067.js} +11 -9
  43. package/index.js +21 -21
  44. package/{library.command-sv6g1h2g.js → library.command-tz7vnwea.js} +3 -3
  45. package/{localRegistry.command-zppa6w7q.js → localRegistry.command-f39yk7be.js} +8 -8
  46. package/{module.command-ccdeder9.js → module.command-qhc9ns0s.js} +5 -5
  47. package/{package.command-xmw605b1.js → package.command-z3epnvas.js} +3 -3
  48. package/package.json +2 -2
  49. package/{page.command-ky7vafsn.js → page.command-exd2vakz.js} +3 -3
  50. package/{primitive.command-bh6j8nfs.js → primitive.command-0w6cha4j.js} +6 -6
  51. package/{quality.command-rqqrpe98.js → quality.command-je4kc9yv.js} +2 -2
  52. package/{repair.command-n9nsm9qe.js → repair.command-knvwfgtg.js} +5 -5
  53. package/{scalar.command-t09gmck8.js → scalar.command-60nxfchc.js} +4 -4
  54. package/{subspace.command-gx2cjpnv.js → subspace.command-0e53tvxk.js} +4 -4
  55. package/templates/server.ts +1 -1
  56. package/{tunnel.command-4g95vhsn.js → tunnel.command-7m2kxtat.js} +2 -2
  57. package/{workflow.command-b91ph8sx.js → workflow.command-j5tk13ds.js} +10 -10
  58. package/{workspace.command-1r29qzzk.js → workspace.command-wypqk2zb.js} +20 -20
  59. package/cloud.script-wrw1kz6k.js +0 -20
  60. package/context.runner-nfs8ksh3.js +0 -21
  61. package/index-y736f42m.js +0 -556
@@ -28,7 +28,7 @@ you fetch on demand — `get_guideline` with the name, or `akan guideline show <
28
28
  | name | covers |
29
29
  |---|---|
30
30
  | `ssrRule` | server-share targets, the `akan.ssr.*` warnings, the client-boundary playbook |
31
- | `runtimeRule` | `web` / `csr` surfaces, gateway vs solo processes, logging, the generated image, shipped assets, database modes |
31
+ | `runtimeRule` | `web` / `csr` surfaces, gateway vs solo processes, logging, the generated image, a desktop app's server, shipped assets, database modes |
32
32
  | `queryRule` | slices and hydration, the generated filter methods, full-text search, cascade removal |
33
33
  | `transportRule` | guards across HTTP and websocket, socket identity and cleanup, binary pubsub, mutation verbs |
34
34
  | `mcpRule` | MCP configuration, wire behaviour, resource URIs, OAuth metadata, protocol revisions |
@@ -57,8 +57,9 @@ back.
57
57
  `no-inline-color`, `no-interpolated-arbitrary-class`)
58
58
  - **Never `throw new Error`.** Throw `new Err("<module>.error.<key>")` and register the key as `[en, ko]` in that
59
59
  module's dictionary `.error({})`. Import `Err` from `"../dict"` on the server and from `"@libs/<lib>/client"` or
60
- `"@apps/<app>/client"` in UI. `no-throw-raw-error.grit` exempts tests, `*.constant.ts`, `common/**`, and `env/**`
61
- — the last two have no legal `Err` import path, so keep throwing code out of them.
60
+ `"@apps/<app>/client"` in UI. `no-throw-raw-error.grit` exempts tests, `*.constant.ts`, `common/**`, `env/**`, and
61
+ a root `native/` folder — `common/` and `env/` have no legal `Err` import path, so keep throwing code out of them,
62
+ and a native plugin throws `AkanNativeError`, the error its bridge carries back to the page.
62
63
  - **Never import a third-party package** from `page/**`, from any barrel, or from any
63
64
  `*.{constant,dictionary,document,service,signal,store}.ts` / `*.{Template,Unit,Util,View,Zone}.tsx`
64
65
  (`no-import-external-library.grit`). Re-export the symbol through a lib first. One-line re-export shims in a lib's
@@ -87,7 +88,8 @@ back.
87
88
  `Notification.requestPermission` / `.permission` (no app page has them), `navigator.geolocation` (use
88
89
  `useGeoLocation` from `akanjs/webkit`) and `navigator.vibrate` (iOS has none; use `haptics`). They are legal on the
89
90
  web, so the rule does not ban them: it keeps them in a `webkit/` hook that branches on `isNativeApp()` from
90
- `akanjs/client/native`, which is where the app side goes too.
91
+ `akanjs/client/native`, which is where the app side goes too. A root `native/` folder is out of its scope: a
92
+ plugin's web part is where those APIs belong.
91
93
  - **Never return a value from a store action** (`no-return-in-store-action.grit`). Every method of a `store(...)`
92
94
  class dispatches through `st.do.<action>()`, typed `void` / `Promise<void>`, so the value is unreachable — write
93
95
  it into state with `this.set({ ... })`. A bare `return;` guard, a `return` inside a nested callback, a getter,
@@ -348,6 +350,15 @@ Full contract: `get_guideline` with `runtimeRule`, or `akan guideline show runti
348
350
  assembles one from; the string form takes no contributions. The generated image installs `ca-certificates` and
349
351
  `tzdata` and nothing else, so an app needing `ffmpeg` or Chromium declares it in `preRuns` / `postRuns`. A lib
350
352
  declares the steps its own runtime needs and every mounting app inherits them.
353
+ - **A desktop app's server (`native.desktop.server: true` on its target) gets nothing from `docker`.** An executable the app spawns
354
+ goes in `bin` — per platform, a download checked against its `sha256` or a file beside the config, put first on
355
+ the app's PATH so the server's `spawn("ffmpeg")` finds it, and in `ctx.binDir` for a native plugin — and a package
356
+ that builds itself at install goes in `trustedDependencies`, which the image honours too. Carry a static LGPL ffmpeg: a `--enable-nonfree` build may
357
+ not be redistributed. A file the user picks with `filePicker.pickFiles({ forServer: true })` reaches it as a
358
+ grant, never a copy or a path: `NativeFile.resolve(grant, "read")` in `akanjs/server` asks the shell for it.
359
+ What it carries is readable on the user's computer — `private/`, the one `env.server.<env>.ts` of its
360
+ `--env` and the server env defaults of its libs — and it has no `public/`: read runtime files from
361
+ `AKAN_APP_DIR`, never `process.cwd()`.
351
362
  - **`assets: { pruneFonts, keepFonts }`** trims from the `dist` copy of `public/` the fonts nothing reads; source
352
363
  trees are never touched. A font with `optimize` on is a build input, not a runtime asset. `keepFonts` belongs to
353
364
  the `akan.config.ts` that owns the font, written against that scope's own `public/`.
@@ -872,6 +883,12 @@ shape, so `cascade` never means "related" — it means one of exactly these:
872
883
  `.layoutStyle()` on the root layout; `.prompt()` on a page. A named export beside the chain, or `page()` in a
873
884
  `_layout.tsx`, fails the build. `.head()` takes JSX — `<title>`, `<meta>`, `<link>` — or a function of the
874
885
  route's args returning it; there is no metadata object, and analytics is the app's own script, not a stage.
886
+ - **A CSR bundle (a native app, `?csr=true`) never shows a root layout's own markup.** Pages render into the
887
+ frame, and what the root layout draws around `{children}` stays in a hidden container, so its wrappers cannot
888
+ stack a second screen beside the frame. Its components still mount and their effects run (`<Auth.User />` works).
889
+ Anything a person must see from a root layout — an overlay, a gate, a banner — renders through
890
+ `createPortal(…, document.body)`, as `<Agent.Chat />` and the dialogs already do. A non-root `_layout.tsx` has no
891
+ such limit.
875
892
  - **A page names every `[x]` segment of its path with `.param("x", Type)`** and reads a query key only through
876
893
  `.search("k", Type)`. Values arrive typed — `ID`/`String` → string, `Int`/`Float` → number, `Boolean`, `Date` →
877
894
  Dayjs, an `enumOf` class → its union, `[T]` → array — a path value the type refuses answers not-found, and a
@@ -915,9 +932,9 @@ export default page()
915
932
  ## Akan Sync Conventions (`apps/**`, `libs/**`)
916
933
 
917
934
  - `apps/<appName>` root may only contain these files: `AGENTS.md`, `CLAUDE.md`, `akan.app.json`, `akan.config.ts`, `client.ts`, `main.ts`, `package.json`, `server.ts`, `tsconfig.json`, `tsconfig.tsbuildinfo`.
918
- - `apps/<appName>` root may only contain these folders: `.akan`, `common`, `env`, `lib`, `page`, `plugin`, `private`, `public`, `script`, `secrets`, `srvkit`, `ui`, `webkit`.
935
+ - `apps/<appName>` root may only contain these folders: `.akan`, `common`, `env`, `lib`, `native`, `page`, `plugin`, `private`, `public`, `script`, `secrets`, `srvkit`, `ui`, `webkit`.
919
936
  - `libs/<libName>` root may only contain these files: `AGENTS.md`, `CLAUDE.md`, `README.md`, `akan.config.ts`, `akan.lib.json`, `client.ts`, `index.ts`, `package.json`, `server.ts`, `tsconfig.json`, `tsconfig.spec.json`, `tsconfig.tsbuildinfo`.
920
- - `libs/<libName>` root may only contain these folders: `common`, `env`, `lib`, `page`, `plugin`, `private`, `public`, `srvkit`, `ui`, `webkit`. A library is never booted or packaged as an app, so the run and mobile entries an app carries (`main.ts`, `.akan`, `script`, `secrets`) are rejected there. A Capacitor-era `ios` / `android` / `mobile` folder or `capacitor.config.*` is refused in an app root too, named as a leftover: the native runtime generates its projects under `.akan/mobile/<target>`.
937
+ - `libs/<libName>` root may only contain these folders: `common`, `env`, `lib`, `native`, `page`, `plugin`, `private`, `public`, `srvkit`, `ui`, `webkit`. A library is never booted or packaged as an app, so the run and mobile entries an app carries (`main.ts`, `.akan`, `script`, `secrets`) are rejected there. A Capacitor-era `ios` / `android` / `mobile` folder or `capacitor.config.*` is refused in an app root too, named as a leftover: the native runtime generates its projects under `.akan/mobile/<target>`.
921
938
  - Both allowlists have one source — `pkgs/@akanjs/devkit/workspaceLayout.ts`. `akan sync` (error), `akan doctor`
922
939
  (diagnostic), and `akan quality scan` (warning) all read it, so add a new root entry there and mirror it into this
923
940
  list, never into one of the three call sites.
@@ -941,12 +958,15 @@ export default page()
941
958
  | Folder | Admission test | Naming |
942
959
  |---|---|---|
943
960
  | `common/` | pure, isomorphic, zero-dependency; may import only sibling `common/*` and `akanjs/base`. Cannot import `Err`, so keep throwing code out of it. | camelCase file, filename equals the single export |
944
- | `webkit/` | touches `window` / `navigator` / the native bridge (`akanjs/client/native`), or is a React hook | `use<Thing>.tsx` — `.tsx` even with no JSX |
961
+ | `webkit/` | touches `window` / `navigator` / the native bridge (`akanjs/client/native`, or `akanjs/client/native/<plugin id>` for any builtin plugin), or is a React hook | `use<Thing>.tsx` — `.tsx` even with no JSX |
945
962
  | `srvkit/` | touches `node:*`, `Bun`, `process.env`, a secret, or a server SDK | camelCase file, PascalCase class |
946
963
  | `ui/` | renders JSX, or defines a look (a recipe in `Recipe/`, a lib's `tokens.css`), and is not bound to one model | PascalCase component, camelCase sidecar (`swipeCard.util.ts`), `Recipe/<name>.ts` |
947
964
  | `plugin/` | build- or CLI-time `AkanPlugin` | `<name>.plugin.ts`, registered in `akan.config.ts` |
965
+ | `native/` | a native plugin the app or lib owns: `native-plugin.json` beside its page API (`src/index.ts`, `definePlugin` from `akanjs/client/native`), its desktop part (`src/desktop.ts`, `defineDesktopPlugin` from `akanjs/native/desktop`), Kotlin and Swift. Every mobile and desktop target ships it; a lib's reach only the apps that depend on it, and the app's own wins an id | `native/<id>/`, the folder named after the plugin id |
948
966
 
949
967
  - Hooks return a named object of async closures, never a tuple.
968
+ - A `native/` plugin's page API is imported by a `webkit/` hook (`../native/<id>/src`) and nothing else; pages and
969
+ components call the hook, the same way they reach `akanjs/client/native`.
950
970
  - `ui/` is the presentation layer — markup and the looks it is built from — so a recipe lives there although it is
951
971
  neither a component nor a hook. Not `webkit/`: its hooks are `"use client"`, the opposite signal for a function
952
972
  server components call. Not `common/`: it cannot import `akanjs/ui`, where `recipe` / `tv` come from.
@@ -1,4 +1,4 @@
1
- # Runtime Rule — Serving, Processes, Logging, Image, Assets
1
+ # Runtime Rule — Serving, Processes, Logging, Image, Desktop Server, Assets
2
2
 
3
3
  How an Akan app is built, what it serves, how many processes it runs, where its logs go, and what ends up in
4
4
  its image. Everything here is declared in `akan.config.ts` or narrowed by an env at boot; none of it is reached
@@ -184,6 +184,112 @@ const config: AppConfig = { docker: "FROM oven/bun:1-slim\n…" }; // verbatim,
184
184
  - `AkanAppConfig.docker` is the resolved declaration; `AkanAppConfig.dockerfile` is the text `akan build` writes
185
185
  to `dist/apps/<app>/Dockerfile`.
186
186
 
187
+ ## A Desktop App's Server — `bin` And `trustedDependencies`
188
+
189
+ A mobile target that declares `native: { desktop: { server: true } }` carries the app's server: `akan build-desktop`,
190
+ `start-desktop --release` and `publish-update` put the backend `akan build` made into the app — the dist `.js`,
191
+ `akan.build.json` and `private/` — and install it on their own with `bun install --production`, and `start-desktop`
192
+ without `--release` starts `akan start` beside the app when no dev server answers. The setting is the app's backend,
193
+ so it does not change under an installed app: an update whose release carries a server when the app has none, or
194
+ none when it has one, is refused, and switching means a reinstall.
195
+ A desktop app builds only for the computer it is built on, so every native addon's prebuild matches the one it runs
196
+ on. It runs as an API-only edge server on SQLite, on a loopback port the launcher picks — the last session's when
197
+ it is free, which is not a guarantee, so a provider that needs an exact redirect URI signs in through a cloud
198
+ server's adapter. It runs in one process, so a `main.ts` asking for replicas (`replica`, `solo: false`) does not
199
+ boot in the app. Its data lives in the app's local data folder (`%LOCALAPPDATA%\<id>\server` on Windows, never
200
+ Roaming; `server-debug` for a `--debug` build). It trusts the OS certificate store and takes the session's
201
+ `HTTP(S)_PROXY` / `NO_PROXY` / `NODE_EXTRA_CA_CERTS`, and nothing else of the user's environment.
202
+
203
+ **Everything it carries is on the user's computer, readable.** That is `private/` as it is, and the server env of
204
+ the one environment the build is for (`--env`: `debug` for `build-desktop`, `main` for `publish-update`, unless
205
+ named): the build keeps `env/env.server.<env>.ts` and swaps every other environment's file for exports that refuse to
206
+ be read. The server env each lib exports as its defaults (spread into every environment by `env.server.type.ts`)
207
+ ships as well. Keep a key in those files only if every user of the app may hold it; a secret the server needs belongs
208
+ to a cloud server its adapter calls. There is no `public/`: a file the server reads at runtime goes in `private/` and is
209
+ read from `AKAN_APP_DIR` (the folder `server.js` sits in), never from `process.cwd()`, which is the app's data
210
+ folder.
211
+
212
+ **Nothing from `docker` reaches it.** `preRuns`, `postRuns` and a whole Dockerfile install into a Linux image the
213
+ desktop app never runs in, and the build warns when an app has them and carries no `bin`. What the server needs
214
+ from them comes one of two ways:
215
+
216
+ ```ts
217
+ const config: AppConfig = {
218
+ trustedDependencies: ["rclnodejs"],
219
+ bin: {
220
+ ffmpeg: {
221
+ "darwin-arm64": {
222
+ url: "https://files.example.com/ffmpeg-7.1-lgpl-darwin-arm64.zip",
223
+ sha256: "…",
224
+ file: "ffmpeg-7.1/bin/ffmpeg",
225
+ },
226
+ "win32-x64": { url: "https://files.example.com/ffmpeg-7.1-lgpl-win64.zip", sha256: "…", file: "bin/ffmpeg.exe" },
227
+ "linux-x64": { path: "tools/linux-x64/ffmpeg" },
228
+ },
229
+ },
230
+ };
231
+ ```
232
+
233
+ - **An executable the desktop app spawns goes in `bin`** — its server's code or its native plugins, whether or not
234
+ it carries a server — keyed by the name the code spawns and then by
235
+ `${process.platform}-${process.arch}` (`darwin-arm64`, `darwin-x64`, `linux-arm64`, `linux-x64`, `win32-arm64`,
236
+ `win32-x64`). A source is `{ url, sha256, file? }` — downloaded when the app is built, refused unless it hashes
237
+ to `sha256` (so plain http is as safe as https), kept in `apps/<app>/.akan/cache/bin/<sha256>` — or
238
+ `{ path, file? }`, relative to the `akan.config.ts` that declares it. `file` is the executable inside an archive
239
+ (`.zip`, `.tar.gz`, `.tgz`, `.tar.xz`, `.tar.bz2`, `.tar`), unpacked with the OS's own `tar` (`unzip` for a zip
240
+ on Linux).
241
+ - Only the building computer's platform is fetched, and an entry without it fails the build instead of the user's
242
+ call. `build-desktop` and `start-desktop` copy the file into the app's `bin/` (keeping `.exe` on Windows), sign it
243
+ with the app on macOS, and put that folder first on the app's PATH. The server's `spawn("ffmpeg")` needs no change
244
+ and finds the carried file even in an app launched from the Finder, whose PATH is only
245
+ `/usr/bin:/bin:/usr/sbin:/sbin`. A native plugin finds the folder in `ctx.binDir`: Bun's own `spawn` and `which`
246
+ without `env` read the environment the app started with, so a plugin passes `env: process.env` to run one by name.
247
+ The server passes it too when it spawns a carried `bun build --compile` executable: the shell starts the server as
248
+ Bun with `BUN_BE_BUN=1`, which akanjs takes out of `process.env` at boot, and a child spawned without `env` still
249
+ inherits it and runs as Bun's own CLI instead of the tool.
250
+ - **One file per entry, so carry a static build.** A build that loads its own shared libraries — a `-shared`
251
+ archive, or Homebrew's ffmpeg with its 55 dylibs — runs on the computer that built it and nowhere else.
252
+ - **A lib's `bin` reaches only the apps that depend on it**, unlike its `docker` steps; the app's own entry of the
253
+ same name wins, and two libs that declare one name differently fail the build.
254
+ - **The image does not read `bin`.** Keep installing through `docker.preRuns` there.
255
+ - **A package that builds itself at install goes in `trustedDependencies`.** `bun install --production` runs no
256
+ dependency's install or postinstall script unless the package is trusted, so an addon with no prebuild arrives
257
+ unbuilt and fails at its first call. The list lands in the built `package.json`, so it applies to the image and to
258
+ the desktop server alike, and a lib's list reaches every app, like its `externalLibs`.
259
+
260
+ **Carry an LGPL ffmpeg.** A build configured with `--enable-nonfree` may not be redistributed at all — the macOS
261
+ binary npm's `ffmpeg-static` downloads is one — and a `--enable-gpl` build obliges you to offer its source. An LGPL
262
+ build has no `libx264`, so encode H.264 through the OS's encoder: `h264_videotoolbox` on macOS, `h264_mf` on
263
+ Windows, VAAPI or NVENC on Linux. Codec patents are a separate question to settle before shipping.
264
+
265
+ **A server bound to its machine stays a service.** A server that needs a whole environment — ROS, system services,
266
+ root to change the network or the clock — runs as a service on that machine (the image), and the desktop app's
267
+ target leaves `desktop.server` off, pinned to it with `AKAN_PUBLIC_SERVER_URL` at build time. A carried server runs
268
+ as the signed-in user and stops with the app, and so does what it started: on macOS and Linux the server leads its
269
+ own process group, which ends with it, also when the app was killed or crashed; on Windows the job object does the
270
+ same.
271
+
272
+ **A file the user picks reaches the server as a grant, never as a copy or a path.** With `native.plugins:
273
+ ["file-picker"]` on the target, `filePicker.pickFiles({ forServer: true })` (also `pickDirectory` and `saveFile`,
274
+ from `akanjs/client/native`) copies nothing, whatever the size: its FileRefs serve the originals for a preview, and
275
+ each result carries a `grant`. The page hands the grant to an endpoint, and the server exchanges it:
276
+
277
+ ```ts
278
+ const input = await NativeFile.resolve(grant, "read"); // "write" for a saveFile grant, "folder" for pickDirectory
279
+ const clip = await NativeFile.resolveIn(folderGrant, "day1/a.mp4"); // refused if it climbs out of the folder
280
+ ```
281
+
282
+ The carried server asks the shell that showed the dialog over its IPC channel, so it reaches what the user picked
283
+ and nothing else, and no page ever holds a path. Behind a dev build `akan start` checks the grant's signature
284
+ against a key in `~/.akan/native` instead, in `operationMode` local only; every other server refuses a grant.
285
+
286
+ **Devices belong to the shell, not the server.** Displays and their changes (`screen`), windows placed on them
287
+ (`window`), the system volume and mute (`volume`), global shortcuts, keep-awake and launch at login are native
288
+ runtime plugins, each added to the target's `native.plugins`. Every builtin plugin's page API is
289
+ `akanjs/client/native/<id>` (`akanjs/client/native/window`, `…/screen`, `…/global-shortcut`), imported in a `webkit/`
290
+ hook; `volume` and `filePicker` also come from `akanjs/client/native` itself. A capability the shell
291
+ lacks is added there: an app's own plugin runs as Bun code in the plugin host and cannot add a native shell op.
292
+
187
293
  ## Database Modes — `database` In `akan.config.ts`
188
294
 
189
295
  | mode | database | cache · queue · pubsub | runs as |
@@ -8,7 +8,7 @@ import {
8
8
  CodegenLock,
9
9
  WorkspaceExecutor,
10
10
  AppExecutor
11
- } from "./index-0n9q1f23.js";
11
+ } from "./index-r8gp8zk3.js";
12
12
  import"./index-52w0fvh8.js";
13
13
  import {
14
14
  GraphClientEntryDiscovery,
@@ -20,7 +20,7 @@ import {
20
20
  DevChangePlanner,
21
21
  DevGeneratedIndexSync,
22
22
  SsrDevBundler
23
- } from "./index-5kjsr2pg.js";
23
+ } from "./index-ttn00pr3.js";
24
24
  import {
25
25
  prepareDevWatchBatch
26
26
  } from "./index-a5vmm6x4.js";