@akanjs/cli 3.0.0-beta.28 → 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.
Files changed (27) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-7acv4ah8.js → CodeAgent-86q8d13j.js} +2 -2
  3. package/{CodeTui-9tkh34mj.js → CodeTui-r8tgxsyb.js} +1 -1
  4. package/akanCode.js +2 -2
  5. package/{application.command-xpnkyx4z.js → application.command-712jd2z8.js} +2 -2
  6. package/{applicationBuildRunner-w4917tje.js → applicationBuildRunner-ez37p161.js} +1 -1
  7. package/{cloud.command-7yd624qd.js → cloud.command-8eegwcsc.js} +3 -3
  8. package/{cloud.script-25vzkz7b.js → cloud.script-55s1cmz9.js} +3 -3
  9. package/{code.command-fpx4gzmy.js → code.command-n0c2tyzx.js} +4 -4
  10. package/{context.command-snn1k5q3.js → context.command-vxf6d7q2.js} +3 -3
  11. package/{context.runner-nzaf8c98.js → context.runner-70r2fmty.js} +2 -2
  12. package/guidelines/conventions/conventions.instruction.md +3 -3
  13. package/guidelines/runtimeRule/runtimeRule.instruction.md +28 -19
  14. package/{index-vt64c067.js → index-16k7m3cs.js} +1 -1
  15. package/{index-tfe3711h.js → index-1hs6jrys.js} +70 -23
  16. package/{index-n9704w6y.js → index-2yvy9ztj.js} +1 -1
  17. package/{index-52n8xr63.js → index-8ej6r02m.js} +1 -1
  18. package/{index-9j0kykc6.js → index-g44da4r2.js} +1 -1
  19. package/{index-ewbek95g.js → index-krwz9ek9.js} +3 -3
  20. package/{index-0ej0sr6m.js → index-n9gxx7bp.js} +38 -17
  21. package/index.js +7 -7
  22. package/{localRegistry.command-f39yk7be.js → localRegistry.command-7dd85czz.js} +2 -2
  23. package/package.json +3 -3
  24. package/{subspace.command-0e53tvxk.js → subspace.command-683qxcw9.js} +1 -1
  25. package/{workspace.command-wypqk2zb.js → workspace.command-jxdm0wrb.js} +5 -5
  26. package/README.ko.md +0 -72
  27. package/README.md +0 -88
package/.build-stamp CHANGED
@@ -1 +1 @@
1
- 4303a4be1f9e80c8d096d61ecbdfc40f9354ddce71c2526133f623a2c8793dec
1
+ 17c8586a89cd04b60d3d62ff58838eecd420ba14c959958abb0d7baf5bd6628c
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  import {
3
3
  CodeAgent2
4
- } from "./index-ewbek95g.js";
5
- import"./index-52n8xr63.js";
4
+ } from "./index-krwz9ek9.js";
5
+ import"./index-8ej6r02m.js";
6
6
  import"./index-7md2g9vw.js";
7
7
  export {
8
8
  CodeAgent2 as CodeAgent
@@ -4,7 +4,7 @@ import {
4
4
  McpServerConfig,
5
5
  McpTokenStore,
6
6
  McpSignIn
7
- } from "./index-52n8xr63.js";
7
+ } from "./index-8ej6r02m.js";
8
8
  import {
9
9
  CodeTuiLines2,
10
10
  CodeTuiAgents2,
package/akanCode.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  CodeMailbox2,
29
29
  CodeSessionFork2,
30
30
  CodeAgent2
31
- } from "./index-ewbek95g.js";
31
+ } from "./index-krwz9ek9.js";
32
32
  import {
33
33
  akanCodeDefaultModel2,
34
34
  akanCodeModel2,
@@ -39,7 +39,7 @@ import {
39
39
  import {
40
40
  SubagentPool2,
41
41
  akanCodePaths2
42
- } from "./index-52n8xr63.js";
42
+ } from "./index-8ej6r02m.js";
43
43
  import"./index-7md2g9vw.js";
44
44
  import {
45
45
  CodeAgentRpcHost2
@@ -11,12 +11,12 @@ import"./index-52w0fvh8.js";
11
11
  import"./index-r8gp8zk3.js";
12
12
  import {
13
13
  getMobileTargetChoices
14
- } from "./index-tfe3711h.js";
14
+ } from "./index-1hs6jrys.js";
15
15
  import"./index-48k96p55.js";
16
16
  import"./index-qtz88kh0.js";
17
17
  import {
18
18
  ApplicationScript
19
- } from "./index-0ej0sr6m.js";
19
+ } from "./index-n9gxx7bp.js";
20
20
  import"./index-7md2g9vw.js";
21
21
  import"./index-nbg9bfnr.js";
22
22
 
@@ -34,7 +34,7 @@ var createServerEnvPlugin = ({ envDir, environment, environments }) => {
34
34
  build.onLoad({ filter }, async (args) => {
35
35
  const name = path.basename(args.path, ".ts").slice("env.server.".length);
36
36
  const { exports } = new Bun.Transpiler({ loader: "ts" }).scan(await Bun.file(args.path).text());
37
- const message = `env/env.server.${name}.ts is not in this build: \`akan build\` bundles the server env of AKAN_PUBLIC_ENV=${environment} alone, the environment its image runs. Build with AKAN_PUBLIC_ENV=${name} to run ${name}.`;
37
+ const message = `env/env.server.${name}.ts is not in this build: \`akan build\` bundles the server env of AKAN_PUBLIC_ENV=${environment} alone, the environment its image runs. To run ${name}, build for it: AKAN_PUBLIC_ENV=${name} in the workspace's .env, which outranks the shell, or --env ${name} on a desktop or mobile build.`;
38
38
  return {
39
39
  loader: "js",
40
40
  contents: [
@@ -12,15 +12,15 @@ import {
12
12
  PlatformTestRun
13
13
  } from "./index-rrt9pnb4.js";
14
14
  import"./index-48k96p55.js";
15
+ import"./index-1hs6jrys.js";
15
16
  import"./index-qtz88kh0.js";
16
- import"./index-0ej0sr6m.js";
17
- import"./index-tfe3711h.js";
17
+ import"./index-n9gxx7bp.js";
18
18
  import"./index-7md2g9vw.js";
19
19
  import"./index-2ahj896a.js";
20
20
  import"./index-k7809wmg.js";
21
21
  import {
22
22
  CloudScript2
23
- } from "./index-9j0kykc6.js";
23
+ } from "./index-g44da4r2.js";
24
24
  import"./index-nbg9bfnr.js";
25
25
 
26
26
  // pkgs/@akanjs/cli/cloud/cloud.command.ts
@@ -1,13 +1,13 @@
1
1
  // @bun
2
2
  import {
3
3
  CloudScript2
4
- } from "./index-9j0kykc6.js";
4
+ } from "./index-g44da4r2.js";
5
5
  import"./index-rrt9pnb4.js";
6
6
  import"./index-k7809wmg.js";
7
- import"./index-0ej0sr6m.js";
7
+ import"./index-n9gxx7bp.js";
8
8
  import"./index-nbg9bfnr.js";
9
9
  import"./index-qtz88kh0.js";
10
- import"./index-tfe3711h.js";
10
+ import"./index-1hs6jrys.js";
11
11
  import"./index-94s080qm.js";
12
12
  import"./index-b68zfsvz.js";
13
13
  import"./index-7md2g9vw.js";
@@ -19,7 +19,7 @@ class CodeRunner extends runner("code") {
19
19
  async serve(options, listen) {
20
20
  await import("./consoleToStderr-h5bryj94.js");
21
21
  const [{ CodeAgent }, { CodeAgentRpcHost }, { CodeAgentRpcListener }] = await Promise.all([
22
- import("./CodeAgent-7acv4ah8.js"),
22
+ import("./CodeAgent-86q8d13j.js"),
23
23
  import("./CodeAgentRpcHost-p7h6n2hp.js"),
24
24
  import("./CodeAgentRpcListener-7p7z5dd7.js")
25
25
  ]);
@@ -34,8 +34,8 @@ class CodeRunner extends runner("code") {
34
34
  }
35
35
  async tui(options, seed) {
36
36
  const [{ CodeAgent }, { CodeTui }] = await Promise.all([
37
- import("./CodeAgent-7acv4ah8.js"),
38
- import("./CodeTui-9tkh34mj.js")
37
+ import("./CodeAgent-86q8d13j.js"),
38
+ import("./CodeTui-r8tgxsyb.js")
39
39
  ]);
40
40
  const agentOptions = await CodeRunner.#agentOptions(options, false);
41
41
  let resume = options.resume;
@@ -60,7 +60,7 @@ class CodeRunner extends runner("code") {
60
60
  }
61
61
  }
62
62
  async run(prompt, options) {
63
- const { CodeAgent } = await import("./CodeAgent-7acv4ah8.js");
63
+ const { CodeAgent } = await import("./CodeAgent-86q8d13j.js");
64
64
  const agent = await CodeAgent.create(await CodeRunner.#agentOptions(options, false));
65
65
  const printer = new CodeAgentStreamPrinter2({ json: options.json, thinking: options.thinking });
66
66
  agent.on((event) => printer.print(event));
@@ -8,8 +8,8 @@ import"./index-r8gp8zk3.js";
8
8
  import"./index-hj6w4ka1.js";
9
9
  import"./index-2s2e5qnd.js";
10
10
  import"./index-0pqzrbpr.js";
11
- import"./index-vt64c067.js";
12
- import"./index-tfe3711h.js";
11
+ import"./index-16k7m3cs.js";
12
+ import"./index-1hs6jrys.js";
13
13
  import"./index-zzvvc845.js";
14
14
  import"./index-2z30v4k5.js";
15
15
  import"./index-ss828r32.js";
@@ -19,7 +19,7 @@ import"./index-2ctqegdm.js";
19
19
  import"./index-h9ed99ax.js";
20
20
  import {
21
21
  ContextScript
22
- } from "./index-n9704w6y.js";
22
+ } from "./index-2yvy9ztj.js";
23
23
 
24
24
  // pkgs/@akanjs/cli/context/context.command.ts
25
25
  class ContextCommand extends command("context", [ContextScript], ({ public: target }) => ({
@@ -1,14 +1,14 @@
1
1
  // @bun
2
2
  import {
3
3
  ContextRunner2
4
- } from "./index-vt64c067.js";
4
+ } from "./index-16k7m3cs.js";
5
5
  import"./index-h9ed99ax.js";
6
6
  import"./index-2ctqegdm.js";
7
7
  import"./index-20r2g1t3.js";
8
8
  import"./index-2z30v4k5.js";
9
9
  import"./index-genegfnt.js";
10
10
  import"./index-ss828r32.js";
11
- import"./index-tfe3711h.js";
11
+ import"./index-1hs6jrys.js";
12
12
  import"./index-0pqzrbpr.js";
13
13
  import"./index-2s2e5qnd.js";
14
14
  import"./index-zzvvc845.js";
@@ -356,9 +356,9 @@ Full contract: `get_guideline` with `runtimeRule`, or `akan guideline show runti
356
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
357
  not be redistributed. A file the user picks with `filePicker.pickFiles({ forServer: true })` reaches it as a
358
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()`.
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()`.
362
362
  - **`assets: { pruneFonts, keepFonts }`** trims from the `dist` copy of `public/` the fonts nothing reads; source
363
363
  trees are never touched. A font with `optimize` on is a build input, not a runtime asset. `keepFonts` belongs to
364
364
  the `akan.config.ts` that owns the font, written against that scope's own `public/`.
@@ -187,27 +187,34 @@ const config: AppConfig = { docker: "FROM oven/bun:1-slim\n…" }; // verbatim,
187
187
  ## A Desktop App's Server — `bin` And `trustedDependencies`
188
188
 
189
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,
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,
193
195
  so it does not change under an installed app: an update whose release carries a server when the app has none, or
194
196
  none when it has one, is refused, and switching means a reinstall.
195
197
  A desktop app builds only for the computer it is built on, so every native addon's prebuild matches the one it runs
196
198
  on. It runs as an API-only edge server on SQLite, on a loopback port the launcher picks — the last session's when
197
199
  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.
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.
211
218
 
212
219
  **Nothing from `docker` reaches it.** `preRuns`, `postRuns` and a whole Dockerfile install into a Linux image the
213
220
  desktop app never runs in, and the build warns when an app has them and carries no `bin`. What the server needs
@@ -265,9 +272,11 @@ Windows, VAAPI or NVENC on Linux. Codec patents are a separate question to settl
265
272
  **A server bound to its machine stays a service.** A server that needs a whole environment — ROS, system services,
266
273
  root to change the network or the clock — runs as a service on that machine (the image), and the desktop app's
267
274
  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.
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.
271
280
 
272
281
  **A file the user picks reaches the server as a grant, never as a copy or a path.** With `native.plugins:
273
282
  ["file-picker"]` on the target, `filePicker.pickFiles({ forServer: true })` (also `pickDirectory` and `saveFile`,
@@ -26,7 +26,7 @@ import {
26
26
  appIdsOf,
27
27
  NativeApp,
28
28
  isPlaceholderAppId
29
- } from "./index-tfe3711h.js";
29
+ } from "./index-1hs6jrys.js";
30
30
  import {
31
31
  Prompter2
32
32
  } from "./index-zzvvc845.js";
@@ -80,7 +80,6 @@ var targetHtmlFilename = (target) => {
80
80
  };
81
81
 
82
82
  // pkgs/@akanjs/devkit/mobile/nativeApp.ts
83
- import { mkdir as mkdir3 } from "fs/promises";
84
83
  import path6 from "path";
85
84
 
86
85
  // pkgs/@akanjs/devkit/mobile/desktopBin.ts
@@ -573,11 +572,14 @@ ${html}`;
573
572
  ${script}${html.slice(at)}`;
574
573
  }
575
574
  static #targetTag = "<script>window.__AKAN_MOBILE_TARGET__=";
575
+ async clear() {
576
+ await rm2(this.dir, { recursive: true, force: true });
577
+ await mkdir2(this.dir, { recursive: true });
578
+ }
576
579
  async assemble(target, { html, publicDir, fontsDir }) {
577
580
  if (!await Bun.file(html).exists())
578
581
  throw new Error(`CSR html for mobile target '${target.name}' not found: ${html}`);
579
- await rm2(this.dir, { recursive: true, force: true });
580
- await mkdir2(this.dir, { recursive: true });
582
+ await this.clear();
581
583
  if (publicDir && await NativeWebDir.#isDir(publicDir))
582
584
  await cp(publicDir, this.dir, { recursive: true });
583
585
  if (fontsDir && await NativeWebDir.#isDir(fontsDir))
@@ -615,6 +617,9 @@ class NativeApp {
615
617
  outDir(platform) {
616
618
  return path6.join(this.targetRoot, "native", platform);
617
619
  }
620
+ devOutDir(platform) {
621
+ return path6.join(this.targetRoot, "dev", platform);
622
+ }
618
623
  async assembleWeb() {
619
624
  const dist = this.app.dist.cwdPath;
620
625
  return await this.web.assemble(this.target.config, {
@@ -658,12 +663,15 @@ class NativeApp {
658
663
  ]);
659
664
  for (const warning of warnings)
660
665
  this.app.logger.warn(warning);
666
+ this.#assertValid(api, config);
667
+ return { api, config };
668
+ }
669
+ #assertValid(api, config) {
661
670
  const problems = api.validateConfig(config, { appDir: this.app.cwdPath });
662
671
  if (problems.length)
663
672
  throw new Error(`Mobile target '${this.target.name}' makes an invalid native config:
664
673
  - ${problems.join(`
665
674
  - `)}`);
666
- return { api, config };
667
675
  }
668
676
  #appLine = (line) => {
669
677
  const read = NativeAppLine.read(line, { verbose: Executor.verbose });
@@ -672,12 +680,12 @@ class NativeApp {
672
680
  else
673
681
  this.app.logger[read.level](read.message);
674
682
  };
675
- #task(platform, config) {
683
+ #task(platform, config, outDir = this.outDir(platform)) {
676
684
  return {
677
685
  appDir: this.app.cwdPath,
678
686
  config,
679
687
  platform,
680
- outDir: this.outDir(platform),
688
+ outDir,
681
689
  envFiles: false,
682
690
  skipWebBuild: true,
683
691
  log: (event) => {
@@ -723,10 +731,10 @@ class NativeApp {
723
731
  const boot = { steps: [], lines: [] };
724
732
  this.#boot = boot;
725
733
  try {
726
- await mkdir3(this.web.dir, { recursive: true });
734
+ await this.web.clear();
727
735
  const [{ api, config }, { api: routes }] = await Promise.all([this.prepare(platform), this.app.getConfig()]);
728
736
  const session = await api.dev({
729
- ...this.#task(platform, config),
737
+ ...this.#task(platform, config, this.devOutDir(platform)),
730
738
  upstream,
731
739
  hmrPath: "/_akan/hmr",
732
740
  wsPaths: [`${routes.prefix}${routes.websocketPrefix}`],
@@ -772,8 +780,7 @@ class NativeApp {
772
780
  }
773
781
  async publishUpdate(platform, { channel, server } = {}) {
774
782
  NativeApp.#assertServerPlatform(platform, server);
775
- if (!this.target.config.updates)
776
- throw new Error(`Mobile target '${this.target.name}' has no updates: { url, publicKey } in akan.config.ts; \`akan update-keygen ${this.app.name}\` prints the key.`);
783
+ this.#assertUpdates();
777
784
  await this.assembleWeb();
778
785
  const { api, config } = await this.prepare(platform, server);
779
786
  return await api.publishUpdate({
@@ -783,6 +790,32 @@ class NativeApp {
783
790
  ...channel ? { channel } : {}
784
791
  });
785
792
  }
793
+ async assertPublishable(platform, { channel, server = false } = {}) {
794
+ this.#assertUpdates();
795
+ const [api, { config }] = await Promise.all([
796
+ NativeApi.load(this.app.cwdPath),
797
+ this.config({ platform, stageBin: false })
798
+ ]);
799
+ this.#assertValid(api, config);
800
+ try {
801
+ api.checkPublishUpdate({
802
+ appDir: this.app.cwdPath,
803
+ config,
804
+ platform,
805
+ out: this.updatesDir,
806
+ server,
807
+ ...channel ? { channel } : {}
808
+ });
809
+ } catch (error) {
810
+ throw new Error(`Mobile target '${this.target.name}': ${error instanceof Error ? error.message : String(error)}`, {
811
+ cause: error
812
+ });
813
+ }
814
+ }
815
+ #assertUpdates() {
816
+ if (!this.target.config.updates)
817
+ throw new Error(`Mobile target '${this.target.name}' has no updates: { url, publicKey } in akan.config.ts; \`akan update-keygen ${this.app.name}\` prints the key.`);
818
+ }
786
819
  async updateKeygen(platform) {
787
820
  const [api, { config }] = await Promise.all([
788
821
  NativeApi.load(this.app.cwdPath),
@@ -874,11 +907,20 @@ var isPlaceholderAppId = (appId) => {
874
907
  return normalized.split(".").some((segment) => placeholderAppIdSegment.test(segment));
875
908
  };
876
909
  // pkgs/@akanjs/devkit/mobile/desktopServerStage.ts
877
- import { cp as cp2, mkdir as mkdir4, readdir as readdir2, rm as rm3, writeFile as writeFile2 } from "fs/promises";
910
+ import { cp as cp2, mkdir as mkdir3, readdir as readdir2, rm as rm3, writeFile as writeFile2 } from "fs/promises";
878
911
  import path7 from "path";
879
912
  class DesktopServerStage {
880
913
  app;
881
- static skippedFiles = new Set(["rscWorker.js", "console.js", "console-runtime.js"]);
914
+ static uncarried = new Set([
915
+ "Dockerfile",
916
+ "package.json",
917
+ "rscWorker.js",
918
+ "console.js",
919
+ "console-runtime.js",
920
+ "csr",
921
+ "public",
922
+ ".akan"
923
+ ]);
882
924
  static rscRenderer = "react-server-dom-webpack";
883
925
  dir;
884
926
  constructor(app) {
@@ -920,22 +962,27 @@ class DesktopServerStage {
920
962
  const dependencies = Object.entries(built.dependencies ?? {}).filter(([name]) => config.externalLibs.includes(name) || name !== DesktopServerStage.rscRenderer && !otherDrivers.has(name));
921
963
  return { ...built, dependencies: Object.fromEntries(dependencies) };
922
964
  }
923
- async prepare(environment) {
965
+ async install() {
924
966
  const config = await this.app.getConfig();
925
967
  DesktopServerStage.assertCarriable(config);
926
- const dist = this.app.dist.cwdPath;
927
968
  await rm3(this.dir, { recursive: true, force: true });
928
- await mkdir4(this.dir, { recursive: true });
929
- const entries = await readdir2(dist, { withFileTypes: true });
930
- for (const entry of entries) {
931
- const carried = entry.isFile() && (entry.name.endsWith(".js") && !DesktopServerStage.skippedFiles.has(entry.name) || entry.name === "akan.build.json") || entry.isDirectory() && entry.name === "private";
932
- if (carried)
933
- await cp2(path7.join(dist, entry.name), path7.join(this.dir, entry.name), { recursive: true });
969
+ await mkdir3(this.dir, { recursive: true });
970
+ await writeFile2(path7.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, config.getProductionPackageJson()), null, 2));
971
+ try {
972
+ await this.app.spawn(process.execPath, ["install", "--production", "--prefer-offline"], { cwd: this.dir });
973
+ } catch (error) {
974
+ throw new Error(`The desktop app's server could not install its packages in ${this.dir}: it needs the npm registry, or a Bun cache that already holds every one of them.
975
+ ${error instanceof Error ? error.message : String(error)}`, { cause: error });
934
976
  }
935
- const built = await Bun.file(path7.join(dist, "package.json")).json();
936
- await writeFile2(path7.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, built), null, 2));
937
- await this.app.spawn(process.execPath, ["install", "--production"], { cwd: this.dir });
938
977
  await rm3(path7.join(this.dir, "node_modules", ".bin"), { recursive: true, force: true });
978
+ }
979
+ async prepare(environment) {
980
+ const config = await this.app.getConfig();
981
+ const dist = this.app.dist.cwdPath;
982
+ await mkdir3(this.dir, { recursive: true });
983
+ for (const name of await readdir2(dist))
984
+ if (!DesktopServerStage.uncarried.has(name))
985
+ await cp2(path7.join(dist, name), path7.join(this.dir, name), { recursive: true });
939
986
  const scanInfo = this.app.getScanInfo({ allowEmpty: true }) ?? await this.app.scan({ write: false });
940
987
  const carried = [...DesktopBin.select(config, scanInfo.getLibs()).keys()];
941
988
  const notice = DesktopServerStage.imageStepsNotice(config, carried);
@@ -10,7 +10,7 @@ import {
10
10
  } from "./index-b68zfsvz.js";
11
11
  import {
12
12
  ContextRunner2
13
- } from "./index-vt64c067.js";
13
+ } from "./index-16k7m3cs.js";
14
14
 
15
15
  // pkgs/@akanjs/cli/context/context.script.ts
16
16
  import { Logger } from "akanjs/common";
@@ -116,7 +116,7 @@ class SubagentPool2 {
116
116
  }
117
117
  }
118
118
  async#run(id, kind, prompt, signal) {
119
- const { CodeAgent } = await import("./CodeAgent-7acv4ah8.js");
119
+ const { CodeAgent } = await import("./CodeAgent-86q8d13j.js");
120
120
  const budget = this.#options.parent.tools.subagent;
121
121
  const child = SubagentPool2.childOf(this.#options.parent, kind);
122
122
  const agent = await CodeAgent.create({
@@ -11,7 +11,7 @@ import {
11
11
  } from "./index-r8gp8zk3.js";
12
12
  import {
13
13
  ApplicationScript
14
- } from "./index-0ej0sr6m.js";
14
+ } from "./index-n9gxx7bp.js";
15
15
  import {
16
16
  PackageScript
17
17
  } from "./index-k7809wmg.js";
@@ -4,7 +4,7 @@ import {
4
4
  akanCodePaths2,
5
5
  McpServerConfig,
6
6
  McpSignIn
7
- } from "./index-52n8xr63.js";
7
+ } from "./index-8ej6r02m.js";
8
8
  import {
9
9
  stripAnsi
10
10
  } from "./index-7md2g9vw.js";
@@ -511,7 +511,7 @@ class AkanToolPack2 {
511
511
  return { name: "akan-tools", factory: async (pi) => await this.#register(pi) };
512
512
  }
513
513
  async names() {
514
- const { ContextRunner } = await import("./context.runner-nzaf8c98.js");
514
+ const { ContextRunner } = await import("./context.runner-70r2fmty.js");
515
515
  return [
516
516
  ...new ContextRunner().listMcpTools(this.#options.mode).map((tool) => tool.name),
517
517
  AkanToolPack2.verifyToolName
@@ -519,7 +519,7 @@ class AkanToolPack2 {
519
519
  }
520
520
  static verifyToolName = "akan_verify";
521
521
  async#register(pi) {
522
- const { ContextRunner } = await import("./context.runner-nzaf8c98.js");
522
+ const { ContextRunner } = await import("./context.runner-70r2fmty.js");
523
523
  const runner = new ContextRunner;
524
524
  const guidelineNames = await AkanToolPack2.#guidelineNames();
525
525
  for (const tool of runner.listMcpTools(this.#options.mode, { guidelineNames })) {
@@ -44,15 +44,15 @@ import {
44
44
  LibExecutor,
45
45
  PkgExecutor
46
46
  } from "./index-r8gp8zk3.js";
47
- import {
48
- LibraryScript
49
- } from "./index-qtz88kh0.js";
50
47
  import {
51
48
  DesktopServerStage,
52
49
  resolveMobileTargets,
53
50
  resolveAppId,
54
51
  NativeApp
55
- } from "./index-tfe3711h.js";
52
+ } from "./index-1hs6jrys.js";
53
+ import {
54
+ LibraryScript
55
+ } from "./index-qtz88kh0.js";
56
56
  import {
57
57
  stripAnsi
58
58
  } from "./index-7md2g9vw.js";
@@ -69,6 +69,7 @@ import { confirm } from "@inquirer/prompts";
69
69
  import { Logger as Logger6 } from "akanjs/common";
70
70
 
71
71
  // pkgs/@akanjs/cli/application/application.runner.ts
72
+ import { realpathSync } from "fs";
72
73
  import path5 from "path";
73
74
 
74
75
  // pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
@@ -860,6 +861,8 @@ class AkanAppHost {
860
861
  message: msg.data.message
861
862
  });
862
863
  this.#sendOrQueueBuildStatus(status);
864
+ if (!status.ok)
865
+ this.#emitDevEvent("failed", status.message);
863
866
  return;
864
867
  }
865
868
  if (backendMsgTypeSet.has(msg.type))
@@ -1864,7 +1867,7 @@ ${uniqueCandidates.map((candidate) => ` - ${candidate}`).join(`
1864
1867
 
1865
1868
  // pkgs/@akanjs/cli/application/application.runner.ts
1866
1869
  import { Logger as Logger3 } from "akanjs/common";
1867
- var loadBuildRunner = async () => (await import("./applicationBuildRunner-w4917tje.js")).ApplicationBuildRunner;
1870
+ var loadBuildRunner = async () => (await import("./applicationBuildRunner-ez37p161.js")).ApplicationBuildRunner;
1868
1871
  var loadPrompts = async () => await import("@inquirer/prompts");
1869
1872
 
1870
1873
  class ApplicationRunner extends runner("application") {
@@ -2087,7 +2090,7 @@ try {
2087
2090
  return;
2088
2091
  }
2089
2092
  const upstream = `http://localhost:${await app.getDevPort()}`;
2090
- if (!await ApplicationRunner.answers(upstream, app.name))
2093
+ if (!await ApplicationRunner.answers(upstream, app.name, app.workspace.workspaceRoot))
2091
2094
  throw new Error(`No dev server answers on ${upstream}; run \`akan start ${app.name}\` first.`);
2092
2095
  const { i18n } = await app.getConfig();
2093
2096
  const session = await nativeApp.dev(platform, { upstream, lang: i18n.defaultLocale, ...selection });
@@ -2109,20 +2112,33 @@ try {
2109
2112
  throw new Error(`start-${platform === "ios" || platform === "android" ? platform : "desktop"} runs one mobile target at a time; pass --target <name>.`);
2110
2113
  return mobileTarget;
2111
2114
  }
2112
- static async answers(url, appName) {
2115
+ static async answers(url, appName, workspaceRoot) {
2113
2116
  const signal = AbortSignal.timeout(3000);
2114
2117
  const health = await fetch(new URL("/_akan/app/health", url), { signal }).catch(() => null);
2115
2118
  if (!health)
2116
2119
  return false;
2117
2120
  const info = await fetch(new URL("/_akan/app/info", url), { signal }).then(async (res) => res.ok ? await res.json() : null).catch(() => null);
2118
- if (info?.appName === appName)
2119
- return true;
2120
2121
  const stopIt = `Stop it (\`akan start ${appName} --kill\` takes the port over) or give ${appName} another port with AKAN_DEV_PORT`;
2122
+ if (info?.appName === appName) {
2123
+ if (typeof info.workspaceRoot !== "string" || ApplicationRunner.#sameCheckout(info.workspaceRoot, workspaceRoot))
2124
+ return true;
2125
+ throw new Error(`${url} is the dev server of ${appName} in ${info.workspaceRoot}, not in ${workspaceRoot}. ${stopIt}.`);
2126
+ }
2121
2127
  if (typeof info?.appName === "string")
2122
2128
  throw new Error(`${url} is the dev server of ${info.appName}, not ${appName}. ${stopIt}.`);
2123
2129
  const { pid } = await health.json().catch(() => null) ?? {};
2124
2130
  throw new Error(typeof pid === "number" ? `${url} is an akan dev server (pid ${pid}) too old to say which app it serves. ${stopIt}.` : `${url} answers, but not as an akan dev server. Stop what holds the port or give ${appName} another port with AKAN_DEV_PORT.`);
2125
2131
  }
2132
+ static #sameCheckout(reported, own) {
2133
+ const canonical = (root) => {
2134
+ try {
2135
+ return realpathSync.native(root);
2136
+ } catch {
2137
+ return path5.resolve(root);
2138
+ }
2139
+ };
2140
+ return canonical(reported) === canonical(own);
2141
+ }
2126
2142
  async releaseIos(app, { target, env = "main", teamId, adHoc = false } = {}) {
2127
2143
  const targets = await resolveMobileTargets(app, target);
2128
2144
  await this.#buildMobileCsr(app, env);
@@ -2176,6 +2192,11 @@ try {
2176
2192
  async publishUpdate(app, platform, { target, env = "main", channel } = {}) {
2177
2193
  const targets = await resolveMobileTargets(app, target);
2178
2194
  const nativePlatform = platform === "desktop" ? NativeApp.desktopPlatform() : platform;
2195
+ for (const mobileTarget of targets)
2196
+ await new NativeApp(app, mobileTarget, env).assertPublishable(nativePlatform, {
2197
+ server: ApplicationRunner.carriesServer(mobileTarget, nativePlatform),
2198
+ ...channel ? { channel } : {}
2199
+ });
2179
2200
  const carried = await this.#stageMobile(app, nativePlatform, targets, env);
2180
2201
  for (const mobileTarget of targets) {
2181
2202
  const nativeApp = new NativeApp(app, mobileTarget, env);
@@ -2200,11 +2221,10 @@ try {
2200
2221
  return platform !== "ios" && platform !== "android" && config.native?.desktop?.server === true;
2201
2222
  }
2202
2223
  async#stageMobile(app, platform, targets, env) {
2203
- const carries = targets.some((mobileTarget) => ApplicationRunner.carriesServer(mobileTarget, platform));
2204
- if (carries)
2205
- DesktopServerStage.assertCarriable(await app.getConfig());
2224
+ const stage = targets.some((mobileTarget) => ApplicationRunner.carriesServer(mobileTarget, platform)) ? new DesktopServerStage(app) : null;
2225
+ await stage?.install();
2206
2226
  await this.#buildMobileCsr(app, env);
2207
- return carries ? await new DesktopServerStage(app).prepare(env) : undefined;
2227
+ return await stage?.prepare(env);
2208
2228
  }
2209
2229
  async#buildMobileCsr(app, env) {
2210
2230
  const prevEnv = {
@@ -3343,12 +3363,13 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
3343
3363
  const mobileTarget = await ApplicationRunner.startTarget(app, undefined, options.target);
3344
3364
  if (!ApplicationRunner.carriesServer(mobileTarget))
3345
3365
  return await this.applicationRunner.startDesktop(app, options);
3366
+ DesktopServerStage.assertCarriable(await app.getConfig());
3346
3367
  const upstream = `http://localhost:${await app.getDevPort()}`;
3347
- if (await ApplicationRunner.answers(upstream, app.name))
3348
- app.log(`The desktop app follows the dev server on ${upstream}.`);
3349
- else
3350
- await this.#startDevServerFor(app, upstream);
3351
3368
  try {
3369
+ if (await ApplicationRunner.answers(upstream, app.name, app.workspace.workspaceRoot))
3370
+ app.log(`The desktop app follows the dev server on ${upstream}.`);
3371
+ else
3372
+ await this.#startDevServerFor(app, upstream);
3352
3373
  await this.applicationRunner.startDesktop(app, { ...options, interrupt: this.#interrupt });
3353
3374
  } finally {
3354
3375
  await this.#interrupt.runAll();
package/index.js CHANGED
@@ -13,18 +13,18 @@ import path from "path";
13
13
 
14
14
  // pkgs/@akanjs/cli/commandModules.ts
15
15
  var commandModules = {
16
- workspace: async () => (await import("./workspace.command-wypqk2zb.js")).WorkspaceCommand,
16
+ workspace: async () => (await import("./workspace.command-jxdm0wrb.js")).WorkspaceCommand,
17
17
  agent: async () => (await import("./agent.command-q9bmqrag.js")).AgentCommand,
18
- code: async () => (await import("./code.command-fpx4gzmy.js")).CodeCommand,
19
- application: async () => (await import("./application.command-xpnkyx4z.js")).ApplicationCommand,
18
+ code: async () => (await import("./code.command-n0c2tyzx.js")).CodeCommand,
19
+ application: async () => (await import("./application.command-712jd2z8.js")).ApplicationCommand,
20
20
  library: async () => (await import("./library.command-tz7vnwea.js")).LibraryCommand,
21
- localRegistry: async () => (await import("./localRegistry.command-f39yk7be.js")).LocalRegistryCommand,
21
+ localRegistry: async () => (await import("./localRegistry.command-7dd85czz.js")).LocalRegistryCommand,
22
22
  package: async () => (await import("./package.command-z3epnvas.js")).PackageCommand,
23
23
  module: async () => (await import("./module.command-qhc9ns0s.js")).ModuleCommand,
24
24
  page: async () => (await import("./page.command-exd2vakz.js")).PageCommand,
25
- context: async () => (await import("./context.command-snn1k5q3.js")).ContextCommand,
26
- cloud: async () => (await import("./cloud.command-7yd624qd.js")).CloudCommand,
27
- subspace: async () => (await import("./subspace.command-0e53tvxk.js")).SubspaceCommand,
25
+ context: async () => (await import("./context.command-vxf6d7q2.js")).ContextCommand,
26
+ cloud: async () => (await import("./cloud.command-8eegwcsc.js")).CloudCommand,
27
+ subspace: async () => (await import("./subspace.command-683qxcw9.js")).SubspaceCommand,
28
28
  guideline: async () => (await import("./guideline.command-ene55fkr.js")).GuidelineCommand,
29
29
  scalar: async () => (await import("./scalar.command-60nxfchc.js")).ScalarCommand,
30
30
  primitive: async () => (await import("./primitive.command-0w6cha4j.js")).PrimitiveCommand,
@@ -10,11 +10,11 @@ import {
10
10
  PkgExecutor
11
11
  } from "./index-r8gp8zk3.js";
12
12
  import"./index-48k96p55.js";
13
+ import"./index-1hs6jrys.js";
13
14
  import"./index-qtz88kh0.js";
14
15
  import {
15
16
  ApplicationScript
16
- } from "./index-0ej0sr6m.js";
17
- import"./index-tfe3711h.js";
17
+ } from "./index-n9gxx7bp.js";
18
18
  import"./index-7md2g9vw.js";
19
19
  import"./index-94s080qm.js";
20
20
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "3.0.0-beta.28",
3
+ "version": "3.0.0-beta.29",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -39,7 +39,7 @@
39
39
  "@earendil-works/pi-tui": "0.86.1",
40
40
  "@inquirer/prompts": "^8.4.3",
41
41
  "@tailwindcss/node": "^4.3.0",
42
- "akanjs": "3.0.0-beta.28",
42
+ "akanjs": "3.0.0-beta.29",
43
43
  "chalk": "^5.6.2",
44
44
  "commander": "^14.0.3",
45
45
  "dayjs": "^1.11.20",
@@ -62,4 +62,4 @@
62
62
  "bun": {
63
63
  "platform": "bun"
64
64
  }
65
- }
65
+ }
@@ -716,7 +716,7 @@ class SubspaceRunner extends runner("subspace") {
716
716
  if (!asked)
717
717
  return { name: subspace.name, workspaceId, host, apps, libs: [], outcome: "cancelled", files: [] };
718
718
  const { libs } = await subspace.slice();
719
- const { CloudScript } = await import("./cloud.script-25vzkz7b.js");
719
+ const { CloudScript } = await import("./cloud.script-55s1cmz9.js");
720
720
  const { files } = await CommandContainer.get(CloudScript).uploadEnv(workspace, {
721
721
  host,
722
722
  workspaceId,
@@ -39,13 +39,13 @@ import {
39
39
  AgentScript
40
40
  } from "./index-rbeahw6j.js";
41
41
  import"./index-48k96p55.js";
42
+ import"./index-1hs6jrys.js";
42
43
  import {
43
44
  LibraryScript
44
45
  } from "./index-qtz88kh0.js";
45
46
  import {
46
47
  ApplicationScript
47
- } from "./index-0ej0sr6m.js";
48
- import"./index-tfe3711h.js";
48
+ } from "./index-n9gxx7bp.js";
49
49
  import"./index-7md2g9vw.js";
50
50
  import"./index-2ahj896a.js";
51
51
  import {
@@ -57,8 +57,8 @@ import {
57
57
  } from "./index-rrt9pnb4.js";
58
58
  import {
59
59
  CloudScript2
60
- } from "./index-9j0kykc6.js";
61
- import"./index-vt64c067.js";
60
+ } from "./index-g44da4r2.js";
61
+ import"./index-16k7m3cs.js";
62
62
  import"./index-2z30v4k5.js";
63
63
  import"./index-ss828r32.js";
64
64
  import"./index-genegfnt.js";
@@ -67,7 +67,7 @@ import"./index-2ctqegdm.js";
67
67
  import"./index-h9ed99ax.js";
68
68
  import {
69
69
  ContextScript
70
- } from "./index-n9704w6y.js";
70
+ } from "./index-2yvy9ztj.js";
71
71
  import"./index-nbg9bfnr.js";
72
72
 
73
73
  // pkgs/@akanjs/cli/workspace/workspace.runner.ts
package/README.ko.md DELETED
@@ -1,72 +0,0 @@
1
- # @akanjs/cli
2
-
3
- [문서](https://akanjs.com/docs) | [npm](https://www.npmjs.com/package/@akanjs/cli) | [런타임](https://www.npmjs.com/package/akanjs)
4
-
5
- Akan.js 워크스페이스를 위한 command-line tooling입니다.
6
-
7
- `@akanjs/cli`는 Akan 애플리케이션, 라이브러리, 프레임워크 패키지를 생성, 빌드, 테스트, 린트,
8
- 릴리즈, 유지보수하는 데 사용하는 `akan` 실행 파일을 제공합니다. Bun-first CLI 패키지이며 Akan
9
- 개발 tooling을 내부에 번들링하므로, 애플리케이션 런타임은 더 작은 `akanjs` 패키지에만 의존할 수
10
- 있습니다.
11
-
12
- ## 설치
13
-
14
- 새 워크스페이스를 바로 만들 수 있습니다.
15
-
16
- ```bash
17
- bunx create-akan-workspace@latest
18
- ```
19
-
20
- 또는 CLI를 전역으로 설치할 수 있습니다.
21
-
22
- ```bash
23
- bun install -g @akanjs/cli@latest
24
- akan --help
25
- ```
26
-
27
- ## 자주 쓰는 명령
28
-
29
- ```bash
30
- akan create-workspace <workspace-name>
31
- akan start <app-name>
32
- akan build <app-name>
33
- akan test <app-or-lib-or-pkg>
34
- akan lint <app-or-lib-or-pkg>
35
- akan create-application <app-name>
36
- akan create-library <lib-name>
37
- akan create-module <module-name>
38
- akan create-scalar <scalar-name>
39
- ```
40
-
41
- 패키지 유지보수 명령도 같은 실행 파일에서 제공합니다.
42
-
43
- ```bash
44
- akan build-package akanjs
45
- akan build-package @akanjs/cli
46
- akan build-package @akanjs/devkit
47
- akan build-package create-akan-workspace
48
- akan verify-akan-publish-packages
49
- akan smoke-registry --test=true --tag=rc
50
- ```
51
-
52
- Akan framework 패키지는 반드시 `dist/pkgs/*` 산출물 기준으로 publish합니다. `verify-akan-publish-packages`는
53
- 빌드된 패키지에 `npm pack --dry-run --json`을 실행하고, `deploy-akan` 또는 local registry smoke 전에 필요한
54
- metadata를 검증합니다.
55
- 저장소 릴리즈에서는 root `akan` bootstrap script가 CLI dist를 덮어쓰지 않도록
56
- `bun run release:build-packages && bun run release:verify-packages`를 우선 사용합니다.
57
-
58
- ## 패키지 경계
59
-
60
- - 애플리케이션과 런타임 코드는 `akanjs`를 사용합니다.
61
- - 사용자-facing 실행 패키지는 `@akanjs/cli`입니다.
62
- - `@akanjs/devkit`은 published CLI 사용을 위해 CLI 안에 번들링됩니다. 일반 CLI 사용자가 별도의
63
- runtime dependency로 설치할 필요는 없습니다.
64
-
65
- ## 요구사항
66
-
67
- - [Bun](https://bun.sh) `>=1.4.0`
68
- - TypeScript 기반 Akan 워크스페이스
69
-
70
- ## 라이선스
71
-
72
- MIT
package/README.md DELETED
@@ -1,88 +0,0 @@
1
- # @akanjs/cli
2
-
3
- [Docs](https://akanjs.com/docs) | [npm](https://www.npmjs.com/package/@akanjs/cli) | [Runtime](https://www.npmjs.com/package/akanjs)
4
-
5
- Command-line tooling for Akan.js workspaces.
6
-
7
- `@akanjs/cli` provides the `akan` executable used to create, build, test, lint, release, and maintain Akan
8
- applications, libraries, and framework packages. It is a Bun-first CLI package and bundles Akan development
9
- tooling internally so application runtimes can depend on the smaller `akanjs` package.
10
-
11
- ## Install
12
-
13
- Create a new workspace:
14
-
15
- ```bash
16
- bunx create-akan-workspace@latest
17
- ```
18
-
19
- Or install the CLI globally:
20
-
21
- ```bash
22
- bun install -g @akanjs/cli@latest
23
- akan --help
24
- ```
25
-
26
- ## Common Commands
27
-
28
- ```bash
29
- akan create-workspace <workspace-name>
30
- akan start <app-name>
31
- akan build <app-name>
32
- akan test <app-or-lib-or-pkg>
33
- akan lint <app-or-lib-or-pkg>
34
- akan create-application <app-name>
35
- akan create-library <lib-name>
36
- akan create-module <module-name>
37
- akan create-scalar <scalar-name>
38
- ```
39
-
40
- ## Agent And MCP Commands
41
-
42
- ```bash
43
- akan context --format markdown
44
- akan context --format json --module <module-name>
45
- akan doctor --format json
46
- akan guideline list
47
- akan guideline show framework
48
- akan agent install cursor
49
- akan mcp
50
- ```
51
-
52
- These commands expose Akan workspace structure, module abstracts, diagnostics, and guideline instructions for
53
- coding agents. The MCP server is read-only and is intended to provide context rather than edit files directly.
54
-
55
- Package maintenance commands are also exposed through the same executable:
56
-
57
- ```bash
58
- akan build-package akanjs
59
- akan build-package @akanjs/cli
60
- akan build-package @akanjs/devkit
61
- akan build-package create-akan-workspace
62
- akan verify-akan-publish-packages
63
- akan smoke-registry --test=true --tag=rc
64
- ```
65
-
66
- Publish Akan framework packages from `dist/pkgs/*` only. `verify-akan-publish-packages` runs `npm pack --dry-run --json`
67
- against the built packages and checks metadata that must be correct before `deploy-akan` or local registry smoke.
68
- For repository releases, prefer `bun run release:build-packages && bun run release:verify-packages` so the CLI package
69
- artifact is built last and is not overwritten by the root `akan` bootstrap script.
70
-
71
- ## Package Boundary
72
-
73
- - Use `akanjs` from application and runtime code.
74
- - Use `@akanjs/cli` as the user-facing executable package.
75
- - `@akanjs/devkit` is bundled into the CLI for published CLI usage; it is not required as a separate
76
- runtime dependency for ordinary CLI users.
77
- - The `akan code` agent follows that same split: its engine, tools and skills are a devkit facet
78
- (`@akanjs/devkit/codeAgent`), and `@akanjs/cli/code` is the terminal host plus the SDK entry that
79
- re-exports both.
80
-
81
- ## Requirements
82
-
83
- - [Bun](https://bun.sh) `>=1.4.0`
84
- - A TypeScript Akan workspace
85
-
86
- ## License
87
-
88
- MIT