@akanjs/cli 3.0.0-beta.27 → 3.0.0-beta.29
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/.build-stamp +1 -1
- package/{CodeAgent-h19atbr3.js → CodeAgent-86q8d13j.js} +2 -2
- package/{CodeTui-46zygq8p.js → CodeTui-r8tgxsyb.js} +1 -1
- package/{agent.command-rp505zxa.js → agent.command-q9bmqrag.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-1p0cpt6y.js → application.command-712jd2z8.js} +70 -8
- package/{applicationBuildRunner-r6b9rkb8.js → applicationBuildRunner-ez37p161.js} +67 -22
- package/buildBatch.proc.js +3 -3
- package/{cloud.command-ps9dabbg.js → cloud.command-8eegwcsc.js} +9 -9
- package/cloud.script-55s1cmz9.js +20 -0
- package/{code.command-78qw3q3f.js → code.command-n0c2tyzx.js} +6 -6
- package/commandManifest.json +1 -1
- package/{context.command-x5f7t3gk.js → context.command-vxf6d7q2.js} +13 -13
- package/context.runner-70r2fmty.js +21 -0
- package/{guideline.command-d5qmxx29.js → guideline.command-ene55fkr.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +27 -7
- package/guidelines/runtimeRule/runtimeRule.instruction.md +116 -1
- package/incrementalBuilder.proc.js +2 -2
- package/{index-hzy4pe24.js → index-0pqzrbpr.js} +2 -2
- package/{index-w5pet0nq.js → index-16k7m3cs.js} +11 -9
- package/index-1hs6jrys.js +994 -0
- package/{index-6dt6zft6.js → index-20r2g1t3.js} +4 -4
- package/{index-xfgdmm5d.js → index-2ctqegdm.js} +2 -2
- package/{index-d65fzxvd.js → index-2s2e5qnd.js} +1 -1
- package/{index-j9z8vg2n.js → index-2yvy9ztj.js} +4 -4
- package/{index-t02g6dpn.js → index-2z30v4k5.js} +3 -3
- package/{index-vxqe24mp.js → index-8ej6r02m.js} +1 -1
- package/{index-58cyna2s.js → index-b68zfsvz.js} +1 -1
- package/{index-bp9b1g2r.js → index-fy42hyb9.js} +7 -3
- package/{index-tg09x14f.js → index-g44da4r2.js} +5 -5
- package/{index-zazvn45w.js → index-genegfnt.js} +4 -4
- package/{index-da72pfdv.js → index-h9ed99ax.js} +7 -7
- package/{index-per2kqy9.js → index-k7809wmg.js} +2 -2
- package/{index-2zcm7fjd.js → index-krwz9ek9.js} +3 -3
- package/{index-118vq9dh.js → index-n9gxx7bp.js} +247 -47
- package/{index-860zjnr1.js → index-nbg9bfnr.js} +1 -1
- package/{index-5mm3g24t.js → index-qtz88kh0.js} +2 -2
- package/{index-0n9q1f23.js → index-r8gp8zk3.js} +231 -98
- package/{index-ggfhz9ab.js → index-rbeahw6j.js} +3 -3
- package/{index-56xwsgyn.js → index-rrt9pnb4.js} +2 -2
- package/{index-v4hnkqz9.js → index-ss828r32.js} +1 -1
- package/{index-5kjsr2pg.js → index-ttn00pr3.js} +38 -3
- package/index.js +21 -21
- package/{library.command-sv6g1h2g.js → library.command-tz7vnwea.js} +3 -3
- package/{localRegistry.command-zppa6w7q.js → localRegistry.command-7dd85czz.js} +8 -8
- package/{module.command-ccdeder9.js → module.command-qhc9ns0s.js} +5 -5
- package/{package.command-xmw605b1.js → package.command-z3epnvas.js} +3 -3
- package/package.json +3 -3
- package/{page.command-ky7vafsn.js → page.command-exd2vakz.js} +3 -3
- package/{primitive.command-bh6j8nfs.js → primitive.command-0w6cha4j.js} +6 -6
- package/{quality.command-rqqrpe98.js → quality.command-je4kc9yv.js} +2 -2
- package/{repair.command-n9nsm9qe.js → repair.command-knvwfgtg.js} +5 -5
- package/{scalar.command-t09gmck8.js → scalar.command-60nxfchc.js} +4 -4
- package/{subspace.command-gx2cjpnv.js → subspace.command-683qxcw9.js} +4 -4
- package/templates/server.ts +1 -1
- package/{tunnel.command-4g95vhsn.js → tunnel.command-7m2kxtat.js} +2 -2
- package/{workflow.command-b91ph8sx.js → workflow.command-j5tk13ds.js} +10 -10
- package/{workspace.command-1r29qzzk.js → workspace.command-jxdm0wrb.js} +20 -20
- package/README.ko.md +0 -72
- package/README.md +0 -88
- package/cloud.script-wrw1kz6k.js +0 -20
- package/context.runner-nfs8ksh3.js +0 -21
- 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/**`,
|
|
61
|
-
—
|
|
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/` (each lib's too, under `private/libs/<lib>`), the
|
|
360
|
+
one `env.server.<env>.ts` of its `--env` and its libs' server env defaults (`env.server.testing.ts`) — and it has
|
|
361
|
+
no `public/`: read runtime files from `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,121 @@ 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 — everything the
|
|
191
|
+
backend build wrote (the `.js`, `akan.build.json`, `private/`, a bundled package's `.node`, `.wasm` or file asset)
|
|
192
|
+
but the Dockerfile, the RSC worker, the console, `csr/` and `public/` — and install its packages first with
|
|
193
|
+
`bun install --production --prefer-offline`, before `akan build` runs. `start-desktop` without `--release` starts
|
|
194
|
+
`akan start` beside the app when no dev server of this checkout answers. The setting is the app's backend,
|
|
195
|
+
so it does not change under an installed app: an update whose release carries a server when the app has none, or
|
|
196
|
+
none when it has one, is refused, and switching means a reinstall.
|
|
197
|
+
A desktop app builds only for the computer it is built on, so every native addon's prebuild matches the one it runs
|
|
198
|
+
on. It runs as an API-only edge server on SQLite, on a loopback port the launcher picks — the last session's when
|
|
199
|
+
it is free, which is not a guarantee, so a provider that needs an exact redirect URI signs in through a cloud
|
|
200
|
+
server's adapter. It refuses a `Host` other than its own, but any program on the computer can still call that port:
|
|
201
|
+
guard its endpoints as a network server's. It runs in one process, so a `main.ts` or an env asking for replicas (`replica`,
|
|
202
|
+
`solo: false`, `AKAN_SOLO=false`, `AKAN_REPLICA`, `AKAN_COMMAND_TYPE=start`) does not boot in the app. Its data lives in the app's local data folder (`%LOCALAPPDATA%\<id>\server`
|
|
203
|
+
on Windows, never Roaming; `server-debug` for a `--debug` build). It trusts the OS certificate store, and of the
|
|
204
|
+
user's environment it takes a fixed set and nothing else: the system's own (`PATH`, `HOME`, the temp folder, `LANG`,
|
|
205
|
+
`TZ`, Windows' folders and `ComSpec`), the desktop session a `bin` tool needs for the screen or the audio server
|
|
206
|
+
(`DISPLAY`, `XAUTHORITY`, `WAYLAND_DISPLAY`, the session bus, `PULSE_*`, `XDG_*`), and the proxy and CA variables
|
|
207
|
+
(`HTTP(S)_PROXY`, `NO_PROXY`, `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`).
|
|
208
|
+
|
|
209
|
+
**Everything it carries is on the user's computer, readable.** That is `private/` as it is — each lib's too, under
|
|
210
|
+
`private/libs/<lib>` — and the server env of the one environment the build is for (`--env`: `debug` for
|
|
211
|
+
`build-desktop`, `local` for `start-desktop --release`, `main` for `publish-update`, unless named): the build keeps
|
|
212
|
+
`env/env.server.<env>.ts` and swaps every other environment's file for exports that refuse to be read. The server env
|
|
213
|
+
each lib exports as its defaults (the lib's `env.server.testing.ts`, spread into every environment by
|
|
214
|
+
`env.server.type.ts`) ships as well. Keep a key in those files only if every user of the app may hold it; a secret the
|
|
215
|
+
server needs belongs to a cloud server its adapter calls. There is no `public/`: a file the server reads at runtime
|
|
216
|
+
goes in `private/` and is read from `AKAN_APP_DIR` (the folder `server.js` sits in), never from `process.cwd()`, which
|
|
217
|
+
is the app's data folder.
|
|
218
|
+
|
|
219
|
+
**Nothing from `docker` reaches it.** `preRuns`, `postRuns` and a whole Dockerfile install into a Linux image the
|
|
220
|
+
desktop app never runs in, and the build warns when an app has them and carries no `bin`. What the server needs
|
|
221
|
+
from them comes one of two ways:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const config: AppConfig = {
|
|
225
|
+
trustedDependencies: ["rclnodejs"],
|
|
226
|
+
bin: {
|
|
227
|
+
ffmpeg: {
|
|
228
|
+
"darwin-arm64": {
|
|
229
|
+
url: "https://files.example.com/ffmpeg-7.1-lgpl-darwin-arm64.zip",
|
|
230
|
+
sha256: "…",
|
|
231
|
+
file: "ffmpeg-7.1/bin/ffmpeg",
|
|
232
|
+
},
|
|
233
|
+
"win32-x64": { url: "https://files.example.com/ffmpeg-7.1-lgpl-win64.zip", sha256: "…", file: "bin/ffmpeg.exe" },
|
|
234
|
+
"linux-x64": { path: "tools/linux-x64/ffmpeg" },
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- **An executable the desktop app spawns goes in `bin`** — its server's code or its native plugins, whether or not
|
|
241
|
+
it carries a server — keyed by the name the code spawns and then by
|
|
242
|
+
`${process.platform}-${process.arch}` (`darwin-arm64`, `darwin-x64`, `linux-arm64`, `linux-x64`, `win32-arm64`,
|
|
243
|
+
`win32-x64`). A source is `{ url, sha256, file? }` — downloaded when the app is built, refused unless it hashes
|
|
244
|
+
to `sha256` (so plain http is as safe as https), kept in `apps/<app>/.akan/cache/bin/<sha256>` — or
|
|
245
|
+
`{ path, file? }`, relative to the `akan.config.ts` that declares it. `file` is the executable inside an archive
|
|
246
|
+
(`.zip`, `.tar.gz`, `.tgz`, `.tar.xz`, `.tar.bz2`, `.tar`), unpacked with the OS's own `tar` (`unzip` for a zip
|
|
247
|
+
on Linux).
|
|
248
|
+
- Only the building computer's platform is fetched, and an entry without it fails the build instead of the user's
|
|
249
|
+
call. `build-desktop` and `start-desktop` copy the file into the app's `bin/` (keeping `.exe` on Windows), sign it
|
|
250
|
+
with the app on macOS, and put that folder first on the app's PATH. The server's `spawn("ffmpeg")` needs no change
|
|
251
|
+
and finds the carried file even in an app launched from the Finder, whose PATH is only
|
|
252
|
+
`/usr/bin:/bin:/usr/sbin:/sbin`. A native plugin finds the folder in `ctx.binDir`: Bun's own `spawn` and `which`
|
|
253
|
+
without `env` read the environment the app started with, so a plugin passes `env: process.env` to run one by name.
|
|
254
|
+
The server passes it too when it spawns a carried `bun build --compile` executable: the shell starts the server as
|
|
255
|
+
Bun with `BUN_BE_BUN=1`, which akanjs takes out of `process.env` at boot, and a child spawned without `env` still
|
|
256
|
+
inherits it and runs as Bun's own CLI instead of the tool.
|
|
257
|
+
- **One file per entry, so carry a static build.** A build that loads its own shared libraries — a `-shared`
|
|
258
|
+
archive, or Homebrew's ffmpeg with its 55 dylibs — runs on the computer that built it and nowhere else.
|
|
259
|
+
- **A lib's `bin` reaches only the apps that depend on it**, unlike its `docker` steps; the app's own entry of the
|
|
260
|
+
same name wins, and two libs that declare one name differently fail the build.
|
|
261
|
+
- **The image does not read `bin`.** Keep installing through `docker.preRuns` there.
|
|
262
|
+
- **A package that builds itself at install goes in `trustedDependencies`.** `bun install --production` runs no
|
|
263
|
+
dependency's install or postinstall script unless the package is trusted, so an addon with no prebuild arrives
|
|
264
|
+
unbuilt and fails at its first call. The list lands in the built `package.json`, so it applies to the image and to
|
|
265
|
+
the desktop server alike, and a lib's list reaches every app, like its `externalLibs`.
|
|
266
|
+
|
|
267
|
+
**Carry an LGPL ffmpeg.** A build configured with `--enable-nonfree` may not be redistributed at all — the macOS
|
|
268
|
+
binary npm's `ffmpeg-static` downloads is one — and a `--enable-gpl` build obliges you to offer its source. An LGPL
|
|
269
|
+
build has no `libx264`, so encode H.264 through the OS's encoder: `h264_videotoolbox` on macOS, `h264_mf` on
|
|
270
|
+
Windows, VAAPI or NVENC on Linux. Codec patents are a separate question to settle before shipping.
|
|
271
|
+
|
|
272
|
+
**A server bound to its machine stays a service.** A server that needs a whole environment — ROS, system services,
|
|
273
|
+
root to change the network or the clock — runs as a service on that machine (the image), and the desktop app's
|
|
274
|
+
target leaves `desktop.server` off, pinned to it with `AKAN_PUBLIC_SERVER_URL` at build time. A carried server runs
|
|
275
|
+
as the signed-in user and stops with the app, and so does what it started. On macOS and Linux the server leads its
|
|
276
|
+
own process group: quitting the app ends the group, and when the app was killed or crashed — even while the server
|
|
277
|
+
was still starting — the server sees its parent gone and ends the group itself, SIGTERM first and SIGKILL a second
|
|
278
|
+
later for whatever ignored it. On Windows the job object ends them all with the app. A process started `detached`
|
|
279
|
+
leaves the group, or the job, and keeps running.
|
|
280
|
+
|
|
281
|
+
**A file the user picks reaches the server as a grant, never as a copy or a path.** With `native.plugins:
|
|
282
|
+
["file-picker"]` on the target, `filePicker.pickFiles({ forServer: true })` (also `pickDirectory` and `saveFile`,
|
|
283
|
+
from `akanjs/client/native`) copies nothing, whatever the size: its FileRefs serve the originals for a preview, and
|
|
284
|
+
each result carries a `grant`. The page hands the grant to an endpoint, and the server exchanges it:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const input = await NativeFile.resolve(grant, "read"); // "write" for a saveFile grant, "folder" for pickDirectory
|
|
288
|
+
const clip = await NativeFile.resolveIn(folderGrant, "day1/a.mp4"); // refused if it climbs out of the folder
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The carried server asks the shell that showed the dialog over its IPC channel, so it reaches what the user picked
|
|
292
|
+
and nothing else, and no page ever holds a path. Behind a dev build `akan start` checks the grant's signature
|
|
293
|
+
against a key in `~/.akan/native` instead, in `operationMode` local only; every other server refuses a grant.
|
|
294
|
+
|
|
295
|
+
**Devices belong to the shell, not the server.** Displays and their changes (`screen`), windows placed on them
|
|
296
|
+
(`window`), the system volume and mute (`volume`), global shortcuts, keep-awake and launch at login are native
|
|
297
|
+
runtime plugins, each added to the target's `native.plugins`. Every builtin plugin's page API is
|
|
298
|
+
`akanjs/client/native/<id>` (`akanjs/client/native/window`, `…/screen`, `…/global-shortcut`), imported in a `webkit/`
|
|
299
|
+
hook; `volume` and `filePicker` also come from `akanjs/client/native` itself. A capability the shell
|
|
300
|
+
lacks is added there: an app's own plugin runs as Bun code in the plugin host and cannot add a native shell op.
|
|
301
|
+
|
|
187
302
|
## Database Modes — `database` In `akan.config.ts`
|
|
188
303
|
|
|
189
304
|
| mode | database | cache · queue · pubsub | runs as |
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
CodegenLock,
|
|
9
9
|
WorkspaceExecutor,
|
|
10
10
|
AppExecutor
|
|
11
|
-
} from "./index-
|
|
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-
|
|
23
|
+
} from "./index-ttn00pr3.js";
|
|
24
24
|
import {
|
|
25
25
|
prepareDevWatchBatch
|
|
26
26
|
} from "./index-a5vmm6x4.js";
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
isScannedRootEntry,
|
|
9
9
|
AppExecutor,
|
|
10
10
|
LibExecutor
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-r8gp8zk3.js";
|
|
12
12
|
import {
|
|
13
13
|
FileSys2
|
|
14
14
|
} from "./index-52w0fvh8.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
workflowPathsForPlan,
|
|
22
22
|
workflowRunArtifactPath,
|
|
23
23
|
workflowSyncDir
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-2s2e5qnd.js";
|
|
25
25
|
|
|
26
26
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
27
27
|
import { readdir } from "fs/promises";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AppExecutor
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-r8gp8zk3.js";
|
|
5
5
|
import {
|
|
6
6
|
jsonText,
|
|
7
7
|
createWorkflowBaselineSummary,
|
|
8
8
|
buildAkanModuleContextIndex,
|
|
9
9
|
toolingRolloutGate
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-2s2e5qnd.js";
|
|
11
11
|
import {
|
|
12
12
|
buildResourceList,
|
|
13
13
|
codexMcpConfigPath,
|
|
@@ -17,24 +17,25 @@ import {
|
|
|
17
17
|
upsertCodexMcpServerBlock,
|
|
18
18
|
renderDoctorText,
|
|
19
19
|
AkanContextAnalyzer
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-0pqzrbpr.js";
|
|
21
21
|
import {
|
|
22
22
|
runner
|
|
23
|
-
} from "./index-
|
|
23
|
+
} from "./index-b68zfsvz.js";
|
|
24
24
|
import {
|
|
25
25
|
getMobileTargets,
|
|
26
|
+
appIdsOf,
|
|
26
27
|
NativeApp,
|
|
27
28
|
isPlaceholderAppId
|
|
28
|
-
} from "./index-
|
|
29
|
+
} from "./index-1hs6jrys.js";
|
|
29
30
|
import {
|
|
30
31
|
Prompter2
|
|
31
32
|
} from "./index-zzvvc845.js";
|
|
32
33
|
import {
|
|
33
34
|
RepairRunner
|
|
34
|
-
} from "./index-
|
|
35
|
+
} from "./index-2z30v4k5.js";
|
|
35
36
|
import {
|
|
36
37
|
WorkflowRunner
|
|
37
|
-
} from "./index-
|
|
38
|
+
} from "./index-h9ed99ax.js";
|
|
38
39
|
|
|
39
40
|
// pkgs/@akanjs/devkit/akanMcpContract.ts
|
|
40
41
|
import path from "path";
|
|
@@ -606,13 +607,14 @@ class ContextRunner2 extends runner("context") {
|
|
|
606
607
|
});
|
|
607
608
|
}
|
|
608
609
|
for (const { name, config: target } of await getMobileTargets(app)) {
|
|
609
|
-
|
|
610
|
+
const placeholder = appIdsOf(target.appId).find(isPlaceholderAppId);
|
|
611
|
+
if (!placeholder)
|
|
610
612
|
continue;
|
|
611
613
|
diagnostics.push({
|
|
612
614
|
severity: "warning",
|
|
613
615
|
code: "mobile-appid-placeholder",
|
|
614
616
|
path: `apps/${appName}/akan.config.ts`,
|
|
615
|
-
message: `Mobile target '${name}' uses placeholder bundle id '${
|
|
617
|
+
message: `Mobile target '${name}' uses placeholder bundle id '${placeholder}'. Apple's developer portal almost always already claims it, so signing to a physical device fails with "cannot be registered to your development team". Set a unique mobile.appId (reverse-DNS of your org).`
|
|
616
618
|
});
|
|
617
619
|
}
|
|
618
620
|
}
|