@akanjs/cli 3.0.0-beta.2 → 3.0.0-beta.21

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 (190) hide show
  1. package/.build-stamp +1 -1
  2. package/CodeAgent-q2m47rqp.js +9 -0
  3. package/CodeAgentRpcHost-p7h6n2hp.js +7 -0
  4. package/CodeAgentRpcListener-7p7z5dd7.js +97 -0
  5. package/CodeTui-pdqhc3sn.js +1457 -0
  6. package/README.md +3 -0
  7. package/TunnelShare-scf4e4pd.js +9 -0
  8. package/{agent.command-d2gfxfck.js → agent.command-ttn09rx3.js} +8 -9
  9. package/akanCode.js +102 -0
  10. package/application.command-7gweenv7.js +179 -0
  11. package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-38exmejd.js} +63 -64
  12. package/buildBatch.proc.js +26 -25
  13. package/cloud.command-bhztw7c2.js +95 -0
  14. package/cloud.script-aabyhgba.js +20 -0
  15. package/code.command-6s8c0k91.js +154 -0
  16. package/commandManifest.json +1 -1
  17. package/consoleToStderr-h5bryj94.js +7 -0
  18. package/context.command-phmgywt2.js +48 -0
  19. package/context.runner-yvyw5g2d.js +21 -0
  20. package/{dependencyScanner-shs82ynp.js → dependencyScanner-ye2e6pdz.js} +2 -2
  21. package/{devTui-40ggmd99.js → devTui-q7k7bakd.js} +122 -40
  22. package/fileSys-5ngz93ga.js +9 -0
  23. package/guideline.command-7jk47fgq.js +42 -0
  24. package/guidelines/agentRule/agentRule.instruction.md +209 -28
  25. package/guidelines/conventions/conventions.instruction.md +96 -29
  26. package/guidelines/fieldRule/fieldRule.instruction.md +1 -0
  27. package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
  28. package/guidelines/queryRule/queryRule.instruction.md +17 -7
  29. package/guidelines/runtimeRule/runtimeRule.instruction.md +77 -3
  30. package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
  31. package/guidelines/transportRule/transportRule.instruction.md +51 -0
  32. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +66 -3
  33. package/incrementalBuilder.proc.js +31 -48
  34. package/{index-h7yw2z9n.js → index-0bq14vr1.js} +8 -14
  35. package/{index-259dsnz3.js → index-1db2cw0y.js} +5 -8
  36. package/index-22d57v4c.js +266 -0
  37. package/index-3hx1z96c.js +87 -0
  38. package/{index-kcxmv48c.js → index-3t7g9e4r.js} +45 -79
  39. package/{index-ne2qy0nk.js → index-4dn1p115.js} +12 -12
  40. package/{index-q0y2jq9j.js → index-5b6tavv3.js} +6 -3
  41. package/index-7cqjvraf.js +69 -0
  42. package/index-7md2g9vw.js +6 -0
  43. package/{index-zry5nz81.js → index-8kb5p54p.js} +51 -30
  44. package/{index-ktb5njgy.js → index-9629w7ns.js} +49 -99
  45. package/index-9dp031by.js +12 -0
  46. package/{index-a64esmtb.js → index-aadpbnt2.js} +172 -308
  47. package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
  48. package/index-c6eggbee.js +123 -0
  49. package/index-d0arnrm8.js +700 -0
  50. package/index-d2242ksy.js +99 -0
  51. package/{index-600zhnve.js → index-dg97qp24.js} +70 -200
  52. package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
  53. package/{index-w8kmx1b6.js → index-fgq75haa.js} +198 -290
  54. package/{index-2tv3fdx4.js → index-ga6hygxr.js} +944 -1333
  55. package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
  56. package/index-hr5n7t4x.js +465 -0
  57. package/{index-hwkygn9s.js → index-khpfpcsd.js} +302 -33
  58. package/{index-7737gxcp.js → index-m0ms3n1c.js} +107 -241
  59. package/{index-ws5e0j51.js → index-mgfx3mfs.js} +43 -26
  60. package/index-p6pbz9n5.js +25 -0
  61. package/index-pp54p5zk.js +53 -0
  62. package/{index-j9yhg404.js → index-q44jbw48.js} +4 -7
  63. package/{index-mak3cd28.js → index-rmcw8yc8.js} +137 -255
  64. package/{index-pnqbkf5p.js → index-rnt66vym.js} +6 -2
  65. package/index-smme4r47.js +49 -0
  66. package/index-tgjy8xf3.js +187 -0
  67. package/index-v5eayqqw.js +2729 -0
  68. package/index-wa5jbxj1.js +760 -0
  69. package/{index-4edzf1gr.js → index-wp43xrjv.js} +77 -150
  70. package/{index-fz5q3gd7.js → index-ya5qbmyg.js} +10 -17
  71. package/index-yjkte9aj.js +1119 -0
  72. package/{index-gyd220p2.js → index-yy4s1j72.js} +554 -577
  73. package/{index-v0qh106m.js → index-z50k041j.js} +580 -862
  74. package/index.js +23 -22
  75. package/{library.command-nbsky672.js → library.command-ccb1cp92.js} +4 -5
  76. package/{localRegistry.command-yeq57wjr.js → localRegistry.command-ajb9n87j.js} +23 -42
  77. package/{module.command-29vqzrh8.js → module.command-5k6h407d.js} +8 -11
  78. package/{package.command-axv0abmn.js → package.command-e6ntvpez.js} +6 -7
  79. package/package.json +14 -6
  80. package/{page.command-vs9s3zrh.js → page.command-v4f90wb8.js} +4 -5
  81. package/{primitive.command-cbxmqbtc.js → primitive.command-g6km57q5.js} +9 -22
  82. package/prompter-ptaa3nk0.js +8 -0
  83. package/{quality.command-gmbs7nws.js → quality.command-de3p36pz.js} +205 -377
  84. package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
  85. package/{repair.command-bqsjptsj.js → repair.command-d3x1javq.js} +11 -35
  86. package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
  87. package/{scalar.command-6k1vhe9s.js → scalar.command-7ezrac2b.js} +7 -10
  88. package/skills/akan-code/SKILL.md +130 -0
  89. package/skills/akan-debug/SKILL.md +42 -0
  90. package/skills/akan-deps/SKILL.md +50 -0
  91. package/skills/akan-err/SKILL.md +39 -0
  92. package/skills/akan-module/SKILL.md +45 -0
  93. package/skills/akan-scaffold/SKILL.md +76 -0
  94. package/skills/akan-scalar/SKILL.md +52 -0
  95. package/skills/akan-service/SKILL.md +34 -0
  96. package/skills/akan-store/SKILL.md +67 -0
  97. package/skills/akan-ui/SKILL.md +63 -0
  98. package/skills/akan-validate/SKILL.md +44 -0
  99. package/{subspace.command-fwjfcts7.js → subspace.command-ps71ynp6.js} +138 -67
  100. package/templates/__scalar/__model__/__model__.abstract.md.template +8 -0
  101. package/templates/appSample/lib/__scalar/workHistory/workHistory.abstract.ts +8 -39
  102. package/templates/appSample/lib/_noti/noti.abstract.ts +6 -29
  103. package/templates/appSample/lib/task/task.abstract.ts +12 -32
  104. package/templates/appSample/lib/task/task.signal.spec.ts +15 -18
  105. package/templates/appSample/lib/task/task.signal.test.ts +15 -1
  106. package/templates/appSample/lib/task/task.signal.ts +1 -1
  107. package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +7 -4
  108. package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +6 -4
  109. package/templates/client.ts +7 -30
  110. package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -1
  111. package/templates/crudPages/[__model__Id]/page.tsx +2 -1
  112. package/templates/crudPages/new/page.tsx +2 -1
  113. package/templates/crudPages/page.tsx +2 -1
  114. package/templates/crudSinglePage/page.tsx +2 -1
  115. package/templates/lib/__lib/lib.dictionary.ts +0 -2
  116. package/templates/lib/__lib/lib.document.ts +0 -2
  117. package/templates/lib/sig.ts +8 -30
  118. package/templates/localDev/docker-compose.yaml.template +3 -14
  119. package/templates/module/__model__.signal.ts +12 -4
  120. package/templates/pkgRoot/tsconfig.json.template +2 -7
  121. package/templates/server.ts +0 -2
  122. package/templates/service/__model__.abstract.md.template +8 -0
  123. package/templates/workspaceRoot/.gitignore.template +0 -1
  124. package/tunnel.command-hsrv2mny.js +129 -0
  125. package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
  126. package/typecheck.proc.js +1 -1
  127. package/workflow.command-z2p32nmf.js +79 -0
  128. package/{workspace.command-8s050m0h.js → workspace.command-tmdqkrh0.js} +77 -109
  129. package/abstractCompactor-mjh5ppsq.js +0 -101
  130. package/application.command-pj2m7584.js +0 -204
  131. package/applicationReleasePackager-4yasxwv9.js +0 -244
  132. package/capacitorApp-36rbr858.js +0 -12
  133. package/cloud.command-jshwdq22.js +0 -93
  134. package/context.command-ez8a0zyz.js +0 -80
  135. package/devTuiView-c08ccbat.js +0 -9
  136. package/getDirname-dr3jz1xg.js +0 -7
  137. package/guideline.command-xmqwx8zm.js +0 -359
  138. package/guidelines/agentRule/agentRule.generate.json +0 -31
  139. package/guidelines/componentRule/componentRule.generate.json +0 -31
  140. package/guidelines/conventions/conventions.generate.json +0 -25
  141. package/guidelines/cssRule/cssRule.generate.json +0 -31
  142. package/guidelines/docPageRule/docPageRule.generate.json +0 -31
  143. package/guidelines/docPageRule/docPageRule.instruction.md +0 -25
  144. package/guidelines/docSyncRule/docSyncRule.generate.json +0 -31
  145. package/guidelines/docSyncRule/docSyncRule.instruction.md +0 -25
  146. package/guidelines/enumConstant/enumConstant.generate.json +0 -31
  147. package/guidelines/enumConstant/enumConstant.instruction.md +0 -28
  148. package/guidelines/fieldRule/fieldRule.generate.json +0 -31
  149. package/guidelines/framework/framework.generate.json +0 -32
  150. package/guidelines/mcpRule/mcpRule.generate.json +0 -35
  151. package/guidelines/modelConstant/modelConstant.generate.json +0 -32
  152. package/guidelines/modelDictionary/modelDictionary.generate.json +0 -32
  153. package/guidelines/modelDocument/modelDocument.generate.json +0 -31
  154. package/guidelines/modelService/modelService.generate.json +0 -31
  155. package/guidelines/modelSignal/modelSignal.generate.json +0 -31
  156. package/guidelines/modelStore/modelStore.generate.json +0 -31
  157. package/guidelines/modelTemplate/modelTemplate.generate.json +0 -31
  158. package/guidelines/modelUnit/modelUnit.generate.json +0 -31
  159. package/guidelines/modelUtil/modelUtil.generate.json +0 -31
  160. package/guidelines/modelView/modelView.generate.json +0 -31
  161. package/guidelines/modelZone/modelZone.generate.json +0 -31
  162. package/guidelines/moduleCodegen/moduleCodegen.generate.json +0 -31
  163. package/guidelines/moduleCodegen/moduleCodegen.instruction.md +0 -28
  164. package/guidelines/moduleOverview/moduleOverview.generate.json +0 -31
  165. package/guidelines/queryRule/queryRule.generate.json +0 -31
  166. package/guidelines/recipeRule/recipeRule.generate.json +0 -31
  167. package/guidelines/runtimeRule/runtimeRule.generate.json +0 -25
  168. package/guidelines/scalarConstant/scalarConstant.generate.json +0 -32
  169. package/guidelines/scalarDictionary/scalarDictionary.generate.json +0 -32
  170. package/guidelines/scalarModule/scalarModule.generate.json +0 -31
  171. package/guidelines/sharedUiUsage/sharedUiUsage.generate.json +0 -31
  172. package/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +0 -25
  173. package/guidelines/ssrRule/ssrRule.generate.json +0 -31
  174. package/guidelines/transportRule/transportRule.generate.json +0 -31
  175. package/guidelines/utilUiUsage/utilUiUsage.generate.json +0 -31
  176. package/guidelines/utilUiUsage/utilUiUsage.instruction.md +0 -25
  177. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +0 -24
  178. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +0 -33
  179. package/index-2hq52jq3.js +0 -465
  180. package/index-47pj3aee.js +0 -76
  181. package/index-6wwzs1bc.js +0 -7
  182. package/index-8pm30m73.js +0 -841
  183. package/index-kn1h7p1x.js +0 -560
  184. package/index-meh558jy.js +0 -65
  185. package/index-rwg2aa4f.js +0 -229
  186. package/index-wt7qrtd4.js +0 -1516
  187. package/templates/__scalar/__model__/__model__.abstract.md +0 -28
  188. package/templates/module/__model__.abstract.md +0 -28
  189. package/templates/service/__model__.abstract.md +0 -28
  190. package/workflow.command-nwpcdc8p.js +0 -107
@@ -376,15 +376,16 @@ export default page()
376
376
  |---|---|
377
377
  | `interface PageProps { params }` + `[x]` folder | `.param("x", ID \| String \| Int \| …)` — one per `[x]` segment, every one of them, in path order |
378
378
  | `searchParams.k` | `.search("k", Type)`; `[Type]` for a repeated key; the value arrives typed and optional |
379
- | `params.lang` | `lang` — on every route's `.render()`, `.head()`, `.metadata()` and `.loading()` with no stage; `.param("lang")` is refused |
379
+ | `params.lang` | `lang` — on every route's `.render()`, `.head()` and `.loading()` with no stage; `.param("lang")` is refused |
380
380
  | `export const pageConfig = {…}` | `.config({…})` — `devOnly` stays a literal |
381
381
  | `export const head = <…/>` / `generateHead({ params })` | `.head(<…/>)` / `.head(({ x }) => <…/>)` |
382
- | `export const metadata` / `generateMetadata` | `.metadata({…})` / `.metadata((args) => …)` |
382
+ | `export const metadata` / `generateMetadata` | `.head(<><title>…</title><meta name="description" content="…" /></>)` — there is no object form |
383
383
  | `export function Loading()` | `.loading((args) => …)` |
384
384
  | `export default async function Page({ params, searchParams })` | `.render(async ({ x, k }) => { …same body… })` — `async` only if it awaits |
385
385
  | `_layout.tsx`: `export default function Layout({ children })` | `layout().render(({ children }) => …)` |
386
386
  | `export function NotFound` / `export function Error` | `.notFound(fn)` / `.error(fn)` on `layout()` |
387
- | root `_layout.tsx`: `fonts`, `theme`, `manifest`, `reconnect`, `wsConnect`, `layoutStyle`, `gaTrackingId` | `rootLayout().fonts([…]).theme("dark").manifest({…}).reconnect(false).wsConnect(false).layoutStyle("web").gaTrackingId("G-…")` |
387
+ | root `_layout.tsx`: `fonts`, `theme`, `manifest`, `reconnect`, `wsConnect`, `layoutStyle` | `rootLayout().fonts([…]).theme("dark").manifest({…}).reconnect(false).wsConnect(false).layoutStyle("web")` |
388
+ | root `_layout.tsx`: `gaTrackingId` | none — render the app's own `"use client"` analytics component from the root layout's `.render()` |
388
389
  | `prompt()` on `endpoint()` (removed) | `.prompt(name, description)` on the page that shows the same screen |
389
390
 
390
391
  Rules the loader and `akan sync` enforce: a chain module has no other export; `page()` belongs in a page file and
@@ -393,6 +394,68 @@ Rules the loader and `akan sync` enforce: a chain module has no other export; `p
393
394
  `_overrides.tsx` is untouched. MCP: `prompts/get` now runs the page's body under the caller's token and hands the
394
395
  model the description, what the page fetched, and the tools of those modules — see `mcpRule`.
395
396
 
397
+ ---
398
+
399
+ ### Recipe 8: Migrating the LLM Adaptor and the Removed `--ai` Commands
400
+
401
+ Two changes land together. The runtime LLM seam now names a wire rather than a vendor, and the CLI's AI editor is
402
+ gone. They touch different files and neither one is optional — the first breaks a build or, worse, degrades
403
+ silently; the second breaks a script that calls a command that no longer exists.
404
+
405
+ #### The adaptor, in `lib/option.ts` and `env/env.server.*.ts`
406
+
407
+ `DeepseekLlm` is deleted. `OpenaiLlm` is the default and speaks the chat-completions dialect to whatever `host`
408
+ names, so DeepSeek, Groq, Together, OpenRouter, Ollama and a self-hosted vLLM are the same class with a different
409
+ `host` and `model`. `AnthropicLlm` is unchanged.
410
+
411
+ ```ts
412
+ // before — env/env.server.local.ts
413
+ export const env: ModulesOptions = {
414
+ ...libEnv,
415
+ llm: { apiKey: process.env.DEEPSEEK_KEY },
416
+ };
417
+
418
+ // after — the default model and host are gone, so name both
419
+ export const env: ModulesOptions = {
420
+ ...libEnv,
421
+ llm: { apiKey: process.env.DEEPSEEK_KEY, model: "deepseek-v4-flash", host: "https://api.deepseek.com" },
422
+ };
423
+ ```
424
+
425
+ | Before | After | What happens if you skip it |
426
+ |---|---|---|
427
+ | `setLlm({ apiKey })`, relying on the DeepSeek default | `setLlm({ apiKey, model, host })` | The app boots, the log warns once, and the chat answers `llmUnavailable` to every turn |
428
+ | `applyAdaptor(LlmAdaptorRole, DeepseekLlm)` | `OpenaiLlm` + `host: "https://api.deepseek.com"` | Build error — the class is gone |
429
+ | `OpenaiLlm` pointed at a **named** `host`, reading images | add `accepts: { image: true }` to `setLlm` | **Silent**: images reach the model as a note saying they could not be read |
430
+ | An adaptor of your own throwing `agent.error.<vendor>RequestFailed` | `new Err("agent.error.llmRequestFailed", { provider, status, reason })` | The chat prints the raw key instead of a sentence |
431
+ | Provider settings in a second `option.use({ myLlmOption })` | put them in `setLlm` and read `use<MyLlmOption>()` | Nothing breaks; the second channel is no longer needed |
432
+
433
+ `OpenaiLlm` claims vision for its default host (`https://api.openai.com/v1`) and for nothing else, because a host
434
+ you named is a gateway it knows nothing about — and bytes handed to a model that cannot decode them kill the whole
435
+ turn on a 400, where text-only degrades them to a note. Declare `accepts` whenever you set `host`.
436
+
437
+ `LlmOption` is now a floor rather than the whole shape: `setLlm` carries whatever else it is handed to the role, so
438
+ an adaptor of your own declares `interface MyLlmOption extends LlmOption { region: string }`, reads it with
439
+ `use<MyLlmOption>()`, and stops needing a config channel beside `setLlm`.
440
+
441
+ #### The CLI, in scripts and CI
442
+
443
+ `AiSession` and every command that drove it are removed. There is no replacement command — the workspace's agent
444
+ surface (`akan context`, the MCP tools, `akan guideline show`) is what feeds a coding agent now.
445
+
446
+ | Removed | What to do instead |
447
+ |---|---|
448
+ | `akan create-module <name> --ai`, `akan create-scalar <name> --ai` | Drop the flag. The command scaffolds the same files it always did; write the constant and dictionary yourself or let a coding agent do it against `get_guideline modelConstant` / `modelDictionary` |
449
+ | `akan compact <app-or-lib>` | Edit the `*.abstract.md` by hand. `akan quality scan` still warns past 300 lines |
450
+ | `akan set-llm`, `akan reset-llm`, `akan ask` | Nothing. The runtime LLM is `option.setLlm(...)` and has never been this config |
451
+ | `akan generate-instruction`, `akan update-instruction`, `akan generate-document`, `akan reapply-instruction` | Nothing. `akan guideline list` / `show` still read the bundled guidelines |
452
+ | `import { AiSession } from "@akanjs/devkit/aiEditor"` | Nothing — the module is gone |
453
+
454
+ `~/.akan/config.json` keeps an `llm` entry if it had one. It is ignored; delete it, because it holds an API key.
455
+
456
+ Then: `bun run akan typecheck <app>`, `bun run akan lint <app>`, and one agent turn in the browser to confirm the
457
+ provider answers.
458
+
396
459
  ### Data Flow Summary
397
460
 
398
461
  For each business question, follow this chain:
@@ -1,22 +1,22 @@
1
1
  // @bun
2
+ import {
3
+ ApplicationBuildReporter,
4
+ HmrWatcher,
5
+ WatchRootResolver
6
+ } from "./index-khpfpcsd.js";
2
7
  import {
3
8
  CodegenLock,
4
9
  WorkspaceExecutor,
5
10
  AppExecutor
6
- } from "./index-2tv3fdx4.js";
7
- import"./index-q0y2jq9j.js";
8
- import"./index-6wwzs1bc.js";
11
+ } from "./index-ga6hygxr.js";
12
+ import"./index-5b6tavv3.js";
9
13
  import {
10
14
  GraphClientEntryDiscovery,
11
15
  RouteClientBuilder,
12
16
  AutoImportSync,
13
17
  DevChangePlanner,
14
18
  DevGeneratedIndexSync
15
- } from "./index-w8kmx1b6.js";
16
- import {
17
- HmrWatcher,
18
- WatchRootResolver
19
- } from "./index-hwkygn9s.js";
19
+ } from "./index-fgq75haa.js";
20
20
  import {
21
21
  prepareDevWatchBatch
22
22
  } from "./index-a5vmm6x4.js";
@@ -30,12 +30,6 @@ import path from "path";
30
30
  import { Logger } from "akanjs/common";
31
31
 
32
32
  class BuildBatchRunner {
33
- static #entryCandidates = (workspaceRoot) => [
34
- path.join(workspaceRoot, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
35
- path.join(workspaceRoot, "node_modules/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
36
- path.join(import.meta.dir, "buildBatch.proc.js"),
37
- path.join(import.meta.dir, "buildBatch.proc.ts")
38
- ];
39
33
  #logger = new Logger("BuildBatchRunner");
40
34
  #entry = null;
41
35
  #workspaceRoot;
@@ -47,7 +41,12 @@ class BuildBatchRunner {
47
41
  async#resolveEntry() {
48
42
  if (this.#entry)
49
43
  return this.#entry;
50
- const candidates = BuildBatchRunner.#entryCandidates(this.#workspaceRoot);
44
+ const candidates = [
45
+ path.join(this.#workspaceRoot, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
46
+ path.join(this.#workspaceRoot, "node_modules/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
47
+ path.join(import.meta.dir, "buildBatch.proc.js"),
48
+ path.join(import.meta.dir, "buildBatch.proc.ts")
49
+ ];
51
50
  for (const candidate of candidates) {
52
51
  if (!await Bun.file(candidate).exists())
53
52
  continue;
@@ -173,6 +172,7 @@ class IncrementalBuilder {
173
172
  app: this.#app,
174
173
  routeId: msg.routeId,
175
174
  seeds: msg.seeds,
175
+ graphSeeds: msg.graphSeeds,
176
176
  artifact: this.#artifact,
177
177
  knownEntries: new Set(msg.knownEntries),
178
178
  discovery: this.#discovery
@@ -195,7 +195,7 @@ class IncrementalBuilder {
195
195
  }
196
196
  };
197
197
  } catch (err) {
198
- const errMsg = err instanceof Error ? err.message : String(err);
198
+ const errMsg = ApplicationBuildReporter.formatError(err, this.#app.workspace.workspaceRoot);
199
199
  this.#logger.error(`build-route failed routeId=${msg.routeId}: ${errMsg}`);
200
200
  this.#sendBuildStatus("route", { generation: msg.generation, ok: false, files: msg.seeds, message: errMsg });
201
201
  return { type: "build-route-res", id: msg.id, ok: false, error: errMsg };
@@ -335,20 +335,19 @@ class IncrementalBuilder {
335
335
  }, 150);
336
336
  }
337
337
  async installWatcher() {
338
- const artifactDir = this.#artifactDir;
339
338
  const roots = await new WatchRootResolver(this.#app).resolve();
340
339
  const watcher = new HmrWatcher({
341
340
  roots,
342
341
  logger: this.#logger,
343
342
  onBatch: async (batch) => {
344
- await this.#enqueueWork("hmr-batch", async () => this.#handleWatchBatch(artifactDir, batch));
343
+ await this.#enqueueWork("hmr-batch", async () => this.#handleWatchBatch(batch));
345
344
  }
346
345
  });
347
346
  await watcher.start();
348
347
  this.#watcher = watcher;
349
348
  this.#logger.verbose(`watching ${roots.length} roots`);
350
349
  }
351
- async#handleWatchBatch(artifactDir, batch) {
350
+ async#handleWatchBatch(batch) {
352
351
  const rawKinds = new Set(batch.kinds);
353
352
  if (rawKinds.size === 0)
354
353
  return;
@@ -401,7 +400,7 @@ class IncrementalBuilder {
401
400
  }
402
401
  const needs = [];
403
402
  if (kinds.includes("code") && rebuildClient) {
404
- if (this.#shouldRebuildCsr())
403
+ if (this.#csrActive)
405
404
  needs.push("csr");
406
405
  else
407
406
  this.#logger.verbose(`csr-rebundle skipped; request /__csr or ?csr=true (or set AKAN_DEV_CSR_REBUILD=1) to enable per-save CSR rebuilds`);
@@ -416,13 +415,10 @@ class IncrementalBuilder {
416
415
  this.#logger.verbose(`css-rebuild scheduled generation=${generation}`);
417
416
  }
418
417
  }
419
- async#runBatch({
420
- generation,
421
- needs,
422
- changedFiles
423
- }) {
418
+ async#runBatch(work) {
419
+ const { generation, needs, changedFiles } = work;
424
420
  const started = Date.now();
425
- const result = await this.#batchRunner.run(await this.#batchRequest({ generation, needs, changedFiles }), (msg) => BuilderChannel.emit(msg));
421
+ const result = await this.#batchRunner.run(await this.#batchRequest(work), (msg) => BuilderChannel.emit(msg));
426
422
  if (result.optimizedFonts)
427
423
  this.#optimizedFonts = result.optimizedFonts;
428
424
  if (result.cssAssets)
@@ -436,11 +432,7 @@ class IncrementalBuilder {
436
432
  this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
437
433
  return result;
438
434
  }
439
- async#batchRequest({
440
- generation,
441
- needs,
442
- changedFiles
443
- }) {
435
+ async#batchRequest({ generation, needs, changedFiles }) {
444
436
  return {
445
437
  appName: this.#app.name,
446
438
  workspaceRoot: this.#app.workspace.workspaceRoot,
@@ -505,9 +497,6 @@ class IncrementalBuilder {
505
497
  await BuilderChannel.send({ type: "build-csr-res", id: msg.id, ok: true });
506
498
  });
507
499
  }
508
- #shouldRebuildCsr() {
509
- return this.#csrActive;
510
- }
511
500
  static #csrArmedByEnv() {
512
501
  return process.env.AKAN_DEV_CSR_REBUILD === "1";
513
502
  }
@@ -606,23 +595,17 @@ class IncrementalBuilder {
606
595
  builder.shutdown(msg.reason);
607
596
  return;
608
597
  }
609
- if (msg.type === "build-route") {
598
+ if (msg.type !== "build-route" && msg.type !== "build-csr")
599
+ return;
600
+ if (!builder || builder.shuttingDown) {
610
601
  const error = builder?.shuttingDown ? recyclingError : bootingError;
611
- if (!builder || builder.shuttingDown) {
612
- BuilderChannel.emit({ type: "build-route-res", id: msg.id, ok: false, error });
613
- return;
614
- }
615
- builder.handleBuildRoute(msg);
602
+ BuilderChannel.emit({ type: `${msg.type}-res`, id: msg.id, ok: false, error });
616
603
  return;
617
604
  }
618
- if (msg.type === "build-csr") {
619
- const error = builder?.shuttingDown ? recyclingError : bootingError;
620
- if (!builder || builder.shuttingDown) {
621
- BuilderChannel.emit({ type: "build-csr-res", id: msg.id, ok: false, error });
622
- return;
623
- }
605
+ if (msg.type === "build-route")
606
+ builder.handleBuildRoute(msg);
607
+ else
624
608
  builder.handleBuildCsr(msg);
625
- }
626
609
  });
627
610
  process.on("disconnect", () => {
628
611
  logger.warn("host IPC channel closed; exiting builder");
@@ -647,7 +630,7 @@ class IncrementalBuilder {
647
630
  await builder.rearmCsrFromEnv();
648
631
  }
649
632
  }
650
- IncrementalBuilder.main().catch((err) => {
633
+ await IncrementalBuilder.main().catch((err) => {
651
634
  console.error(err);
652
635
  process.exit(1);
653
636
  });
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  import {
3
3
  getDirname2
4
- } from "./index-6wwzs1bc.js";
4
+ } from "./index-5b6tavv3.js";
5
5
 
6
6
  // pkgs/@akanjs/devkit/prompter.ts
7
7
  import fsPromise from "fs/promises";
8
8
  import { input, select } from "@inquirer/prompts";
9
- class Prompter {
9
+ class Prompter2 {
10
10
  static async#getGuidelineRoot() {
11
11
  const dirname = getDirname2(import.meta.url);
12
12
  const candidates = [`${dirname}/guidelines`, `${dirname}/../cli/guidelines`];
@@ -19,27 +19,21 @@ class Prompter {
19
19
  return candidates[0];
20
20
  }
21
21
  static async selectGuideline() {
22
- const guideNames = await Prompter.listGuidelines();
22
+ const guideNames = await Prompter2.listGuidelines();
23
23
  return await select({
24
24
  message: "Select a guideline",
25
25
  choices: guideNames.map((name) => ({ name, value: name }))
26
26
  });
27
27
  }
28
28
  static async listGuidelines() {
29
- const guidelineRoot = await Prompter.#getGuidelineRoot();
29
+ const guidelineRoot = await Prompter2.#getGuidelineRoot();
30
30
  return (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_")).sort();
31
31
  }
32
- static async getGuideJson(guideName) {
33
- const guidelineRoot = await Prompter.#getGuidelineRoot();
34
- const filePath = `${guidelineRoot}/${guideName}/${guideName}.generate.json`;
35
- const guideJson = await fsPromise.readFile(filePath, "utf-8");
36
- return JSON.parse(guideJson);
37
- }
38
32
  static async getInstruction(guideName) {
39
- const guideNames = await Prompter.listGuidelines();
33
+ const guideNames = await Prompter2.listGuidelines();
40
34
  if (!guideNames.includes(guideName))
41
35
  throw new Error(`Unknown guideline "${guideName}". Available: ${guideNames.join(", ")}`);
42
- const guidelineRoot = await Prompter.#getGuidelineRoot();
36
+ const guidelineRoot = await Prompter2.#getGuidelineRoot();
43
37
  return await fsPromise.readFile(`${guidelineRoot}/${guideName}/${guideName}.instruction.md`, "utf-8");
44
38
  }
45
39
  static async getUpdateRequest(guideName) {
@@ -67,7 +61,7 @@ ${request}
67
61
  `;
68
62
  }
69
63
  async getDocumentation(guideName) {
70
- const guidelineRoot = await Prompter.#getGuidelineRoot();
64
+ const guidelineRoot = await Prompter2.#getGuidelineRoot();
71
65
  const filePath = `${guidelineRoot}/${guideName}/${guideName}.instruction.md`;
72
66
  const document = await fsPromise.readFile(filePath, "utf-8");
73
67
  return `\`\`\`markdown
@@ -77,4 +71,4 @@ ${document}
77
71
  }
78
72
  }
79
73
 
80
- export { Prompter };
74
+ export { Prompter2 };
@@ -2,23 +2,21 @@
2
2
  import {
3
3
  runner,
4
4
  script
5
- } from "./index-mak3cd28.js";
5
+ } from "./index-rmcw8yc8.js";
6
6
  import {
7
7
  LibExecutor
8
- } from "./index-2tv3fdx4.js";
8
+ } from "./index-ga6hygxr.js";
9
9
  import {
10
10
  LibSource,
11
11
  formatLibStatuses
12
- } from "./index-3d9y066c.js";
12
+ } from "./index-bjwxs904.js";
13
13
 
14
14
  // pkgs/@akanjs/cli/library/library.script.ts
15
15
  import { Logger } from "akanjs/common";
16
16
 
17
17
  // pkgs/@akanjs/devkit/semver.ts
18
18
  var stripRangeOperator = (version) => version.replace(/^[\s^~>=<v]+/, "").trim();
19
- var parseVersion = (version) => {
20
- return version.replace(/^[^\d]*/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
21
- };
19
+ var parseVersion = (version) => version.replace(/^[^\d]*/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
22
20
  var compareNumeric = (a, b) => {
23
21
  const left = parseVersion(a);
24
22
  const right = parseVersion(b);
@@ -44,8 +42,7 @@ class LibraryRunner extends runner("library") {
44
42
  async createLibrary(libName, workspace) {
45
43
  await workspace.mkdir(`libs/${libName}`);
46
44
  await workspace.applyTemplate({ basePath: `libs/${libName}`, template: "libRoot", dict: { libName } });
47
- const lib = LibExecutor.from(workspace, libName);
48
- return lib;
45
+ return LibExecutor.from(workspace, libName);
49
46
  }
50
47
  async removeLibrary(lib) {
51
48
  await lib.workspace.removeDir(`libs/${lib.name}`);
@@ -0,0 +1,266 @@
1
+ // @bun
2
+ import {
3
+ FileSys2
4
+ } from "./index-5b6tavv3.js";
5
+ import {
6
+ ConfigLock
7
+ } from "./index-3hx1z96c.js";
8
+
9
+ // pkgs/@akanjs/devkit/cloud/globalConfig.ts
10
+ import { mkdir, rename, rm, writeFile } from "fs/promises";
11
+ import dayjs from "dayjs";
12
+
13
+ // pkgs/@akanjs/devkit/cloud/constants.ts
14
+ import path from "path";
15
+ var basePath = path.join(Bun.env.HOME ?? Bun.env.USERPROFILE ?? "", ".akan");
16
+ var configPath = path.join(basePath, "config.json");
17
+ var getDefaultHostConfig = (host = GlobalConfig.akanCloudHost) => ({ host });
18
+ var defaultAkanGlobalConfig = {
19
+ cloudHost: {},
20
+ remoteEnvServers: {},
21
+ testTargets: {}
22
+ };
23
+
24
+ // pkgs/@akanjs/devkit/cloud/globalConfig.ts
25
+ class GlobalConfig {
26
+ static akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? `http://localhost:${process.env.CLOUD_HOST_PORT ?? 8283}` : "https://cloud.akanjs.com";
27
+ static async#getAkanGlobalConfig() {
28
+ const exists = await FileSys2.fileExists(configPath);
29
+ const akanConfig = exists ? await FileSys2.readJson(configPath) : {};
30
+ return {
31
+ ...defaultAkanGlobalConfig,
32
+ ...akanConfig,
33
+ cloudHost: akanConfig.cloudHost ?? defaultAkanGlobalConfig.cloudHost,
34
+ remoteEnvServers: akanConfig.remoteEnvServers ?? defaultAkanGlobalConfig.remoteEnvServers,
35
+ testTargets: akanConfig.testTargets ?? defaultAkanGlobalConfig.testTargets
36
+ };
37
+ }
38
+ static async#setAkanGlobalConfig(akanConfig) {
39
+ await mkdir(basePath, { recursive: true, mode: 448 });
40
+ const temp = `${configPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
41
+ try {
42
+ await writeFile(temp, JSON.stringify(akanConfig, null, 2), { mode: 384 });
43
+ await rename(temp, configPath);
44
+ } catch (error) {
45
+ await rm(temp, { force: true });
46
+ throw error;
47
+ }
48
+ }
49
+ static #lockPath = `${configPath}.lock`;
50
+ static async#update(mutate) {
51
+ await ConfigLock.run(GlobalConfig.#lockPath, async () => {
52
+ await GlobalConfig.#setAkanGlobalConfig(mutate(await GlobalConfig.#getAkanGlobalConfig()));
53
+ });
54
+ }
55
+ static async getHostConfig(host = GlobalConfig.akanCloudHost) {
56
+ const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
57
+ return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
58
+ }
59
+ static async setHostConfig(config = getDefaultHostConfig()) {
60
+ await GlobalConfig.#update((akanConfig) => GlobalConfig.#withHost(akanConfig, config));
61
+ }
62
+ static #withHost(akanConfig, config) {
63
+ return {
64
+ ...akanConfig,
65
+ cloudHost: { ...akanConfig.cloudHost, [config.host]: GlobalConfig.toHostConfigDto(config) }
66
+ };
67
+ }
68
+ static async refreshHostAuth(host, refresh) {
69
+ try {
70
+ return await ConfigLock.run(GlobalConfig.#lockPath, async () => {
71
+ const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
72
+ const current = GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
73
+ const accessToken = current.auth?.accessToken;
74
+ const refreshToken = accessToken?.refreshToken;
75
+ if (!accessToken || !refreshToken || !GlobalConfig.needRefreshToken(accessToken))
76
+ return current;
77
+ const claimed = {
78
+ ...current,
79
+ auth: { ...current.auth, accessToken: { ...accessToken, refreshToken: null } }
80
+ };
81
+ await GlobalConfig.#setAkanGlobalConfig(GlobalConfig.#withHost(akanConfig, claimed));
82
+ const next = await refresh(refreshToken).catch(() => null);
83
+ if (!next?.jwt)
84
+ return claimed;
85
+ const refreshed = { ...current, auth: { ...current.auth, accessToken: next } };
86
+ await GlobalConfig.#setAkanGlobalConfig(GlobalConfig.#withHost(await GlobalConfig.#getAkanGlobalConfig(), refreshed));
87
+ return refreshed;
88
+ });
89
+ } catch {
90
+ return await GlobalConfig.getHostConfig(host);
91
+ }
92
+ }
93
+ static async getRemoteEnvServers() {
94
+ return (await GlobalConfig.#getAkanGlobalConfig()).remoteEnvServers;
95
+ }
96
+ static async setRemoteEnvServer(name, config) {
97
+ await GlobalConfig.#update((akanConfig) => ({
98
+ ...akanConfig,
99
+ remoteEnvServers: { ...akanConfig.remoteEnvServers, [name]: config }
100
+ }));
101
+ }
102
+ static async removeRemoteEnvServer(name) {
103
+ await GlobalConfig.#update((akanConfig) => {
104
+ const { [name]: _, ...remoteEnvServers } = akanConfig.remoteEnvServers;
105
+ return { ...akanConfig, remoteEnvServers };
106
+ });
107
+ }
108
+ static async getTestTargets() {
109
+ return (await GlobalConfig.#getAkanGlobalConfig()).testTargets;
110
+ }
111
+ static async setTestTargets(testTargets) {
112
+ await GlobalConfig.#update((akanConfig) => ({
113
+ ...akanConfig,
114
+ testTargets: { ...akanConfig.testTargets, ...testTargets }
115
+ }));
116
+ }
117
+ static needRefreshToken(accessToken) {
118
+ return !!accessToken?.expiresAt?.isBefore(dayjs().add(1, "hour"));
119
+ }
120
+ static toAccessToken(accessToken) {
121
+ return {
122
+ jwt: accessToken.jwt,
123
+ refreshToken: accessToken.refreshToken ?? null,
124
+ expiresAt: accessToken.expiresAt ? dayjs(accessToken.expiresAt) : null
125
+ };
126
+ }
127
+ static toAccessTokenDto(accessToken) {
128
+ return {
129
+ jwt: accessToken.jwt,
130
+ refreshToken: accessToken.refreshToken ?? null,
131
+ expiresAt: accessToken.expiresAt?.toString() ?? null
132
+ };
133
+ }
134
+ static toHostConfigDto(hostConfig) {
135
+ return {
136
+ host: hostConfig.host,
137
+ auth: {
138
+ accessToken: hostConfig.auth?.accessToken ? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken) : undefined,
139
+ self: hostConfig.auth?.self
140
+ }
141
+ };
142
+ }
143
+ static toHostConfig(hostConfigDto) {
144
+ return {
145
+ host: hostConfigDto.host,
146
+ auth: {
147
+ accessToken: hostConfigDto.auth?.accessToken ? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken) : undefined,
148
+ self: hostConfigDto.auth?.self
149
+ }
150
+ };
151
+ }
152
+ }
153
+
154
+ // pkgs/@akanjs/devkit/cloud/cloudApi.ts
155
+ class HttpClient {
156
+ baseUrl;
157
+ headers = {};
158
+ constructor(baseUrl, headers = {}) {
159
+ this.baseUrl = baseUrl;
160
+ this.headers = headers;
161
+ }
162
+ async get(url, { headers, signal } = {}) {
163
+ const response = await fetch(`${this.baseUrl}${url}`, {
164
+ headers: { "Content-Type": "application/json", ...this.headers, ...headers },
165
+ ...signal ? { signal } : {}
166
+ });
167
+ return await HttpClient.#body(response, url);
168
+ }
169
+ async getFile(url, localPath, headers) {
170
+ const response = await fetch(`${this.baseUrl}${url}`, {
171
+ headers: { ...this.headers, ...headers }
172
+ });
173
+ if (!response.ok)
174
+ throw new Error(`Failed to download file: ${response.status} ${response.statusText}`);
175
+ await Bun.write(localPath, response);
176
+ }
177
+ async post(url, data, { headers, signal } = {}) {
178
+ const isFormData = data instanceof FormData;
179
+ const response = await fetch(`${this.baseUrl}${url}`, {
180
+ method: "POST",
181
+ body: isFormData ? data : JSON.stringify(data),
182
+ headers: isFormData ? { ...this.headers, ...headers } : { "Content-Type": "application/json", ...this.headers, ...headers },
183
+ ...signal ? { signal } : {}
184
+ });
185
+ return await HttpClient.#body(response, url);
186
+ }
187
+ static async#body(response, url) {
188
+ const text = await response.text();
189
+ if (!response.ok)
190
+ throw new Error(`${response.status} ${response.statusText} from ${url}: ${text.slice(0, 200)}`);
191
+ return JSON.parse(text);
192
+ }
193
+ setHeaders(headers) {
194
+ Object.assign(this.headers, headers);
195
+ return this;
196
+ }
197
+ }
198
+
199
+ class CloudApi {
200
+ #api;
201
+ #accessToken = null;
202
+ #workspace;
203
+ host;
204
+ url;
205
+ static async fromHost(workspace, host) {
206
+ const hostConfig = await GlobalConfig.getHostConfig(host);
207
+ const accessToken = hostConfig.auth?.accessToken;
208
+ if (!accessToken || !GlobalConfig.needRefreshToken(accessToken))
209
+ return new CloudApi(workspace, hostConfig);
210
+ const tokenless = new CloudApi(workspace, hostConfig);
211
+ const refreshed = await GlobalConfig.refreshHostAuth(hostConfig.host, (refreshToken) => tokenless.refreshAuthToken(refreshToken));
212
+ return new CloudApi(workspace, refreshed);
213
+ }
214
+ constructor(workspace, hostConfig) {
215
+ this.#workspace = workspace;
216
+ this.#accessToken = hostConfig.auth?.accessToken ?? null;
217
+ this.host = hostConfig.host;
218
+ this.url = `${this.host}/api`;
219
+ this.#api = new HttpClient(this.url);
220
+ if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
221
+ this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
222
+ }
223
+ #authorize(accessTokenDto) {
224
+ this.#accessToken = GlobalConfig.toAccessToken(accessTokenDto);
225
+ this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
226
+ return this.#accessToken;
227
+ }
228
+ async uploadEnv(devProjectId, file) {
229
+ const formData = new FormData;
230
+ formData.append("devProjectId", devProjectId);
231
+ formData.append("file", file);
232
+ return await this.#api.post(`/uploadEnv/${devProjectId}`, formData);
233
+ }
234
+ async downloadEnv(devProjectId) {
235
+ const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
236
+ await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
237
+ return localPath;
238
+ }
239
+ async getRemoteAuthToken(remoteId) {
240
+ try {
241
+ return this.#authorize(await this.#api.get(`/getRemoteAuthToken/${remoteId}`));
242
+ } catch (_) {
243
+ return null;
244
+ }
245
+ }
246
+ async refreshAuthToken(refreshToken) {
247
+ return this.#authorize(await this.#api.post(`/refreshAuthToken`, { refreshToken }, { signal: AbortSignal.timeout(20000) }));
248
+ }
249
+ async requestTunnel(input) {
250
+ return await this.#api.post(`/tunnel/requestTunnel`, input);
251
+ }
252
+ async tunnelListInSelf() {
253
+ return await this.#api.get(`/tunnel/tunnelListInSelf`);
254
+ }
255
+ async revokeTunnel(code) {
256
+ return await this.#api.post(`/tunnel/revokeTunnel`, { code }, { signal: AbortSignal.timeout(5000) });
257
+ }
258
+ async getRemoteSelf() {
259
+ try {
260
+ return await this.#api.get(`/getRemoteSelf`);
261
+ } catch {
262
+ return null;
263
+ }
264
+ }
265
+ }
266
+ export { getDefaultHostConfig, GlobalConfig, CloudApi };