@akanjs/cli 3.0.2 → 3.1.0-rc.0

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 (84) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-szbjv4gv.js → CodeAgent-q7a4srt6.js} +2 -2
  3. package/{CodeTui-5nw86ab6.js → CodeTui-ak23dxc9.js} +1 -1
  4. package/{TunnelShare-cwh9ezsm.js → TunnelShare-21mm6dr5.js} +2 -2
  5. package/{agent.command-np1dffkb.js → agent.command-f3gsc1gd.js} +5 -5
  6. package/akanCode.js +2 -2
  7. package/{application.command-gk69v1xs.js → application.command-3v2d53gd.js} +37 -15
  8. package/{applicationBuildRunner-wx09n9g6.js → applicationBuildRunner-1yaqsrdq.js} +115 -103
  9. package/buildBatch.proc.js +16 -5
  10. package/cloud.command-kk2rwkvw.js +187 -0
  11. package/cloud.script-r7eyfey4.js +20 -0
  12. package/{code.command-hmtx14b2.js → code.command-30sca09d.js} +6 -6
  13. package/commandManifest.json +1 -1
  14. package/{context.command-6aqvdx43.js → context.command-k7eqacq6.js} +13 -13
  15. package/context.runner-hqd1ppqe.js +21 -0
  16. package/{guideline.command-axvf68gc.js → guideline.command-dtczrqpf.js} +2 -2
  17. package/guidelines/agentRule/agentRule.instruction.md +10 -2
  18. package/guidelines/conventions/conventions.instruction.md +68 -25
  19. package/guidelines/cssRule/cssRule.instruction.md +19 -0
  20. package/guidelines/mcpRule/mcpRule.instruction.md +67 -12
  21. package/guidelines/queryRule/queryRule.instruction.md +11 -1
  22. package/guidelines/runtimeRule/runtimeRule.instruction.md +97 -16
  23. package/guidelines/ssrRule/ssrRule.instruction.md +10 -7
  24. package/guidelines/transportRule/transportRule.instruction.md +19 -0
  25. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +6 -6
  26. package/incrementalBuilder.proc.js +24 -3
  27. package/{index-h9ht6s60.js → index-05g3jayg.js} +1 -1
  28. package/{index-j38qkvkk.js → index-0bm04n5g.js} +12 -10
  29. package/{index-g38nhv87.js → index-2etcv0sc.js} +1 -1
  30. package/{index-zyw622y1.js → index-2mx692qq.js} +3 -3
  31. package/{index-jb0hmt3k.js → index-2t4tqd1c.js} +3 -3
  32. package/{index-50wb2a26.js → index-55mynnqs.js} +354 -129
  33. package/{index-q6ft9fkc.js → index-6jjtnhkw.js} +8 -3
  34. package/index-6w0rbr9d.js +68 -0
  35. package/{index-2wc41k5w.js → index-7bymavqk.js} +2 -2
  36. package/{index-gtjgnv36.js → index-aprt7pkz.js} +6 -9
  37. package/{index-1bjkfvm9.js → index-c1x0be5j.js} +2 -2
  38. package/{index-z750gk1j.js → index-c2yrsxdh.js} +13 -7
  39. package/{index-cb6yc5mb.js → index-c76k723e.js} +2 -2
  40. package/{index-6keyf4sg.js → index-ctanya41.js} +4 -4
  41. package/{index-f4e08ptp.js → index-e0cvze1s.js} +4 -4
  42. package/{index-hp3stk1m.js → index-fm46xrf1.js} +3 -3
  43. package/{index-3xmzr1p1.js → index-gnyf1fqy.js} +2 -2
  44. package/{index-zwd84m0w.js → index-jyv387qw.js} +4 -4
  45. package/{index-jk9xnck6.js → index-kjvrq3nc.js} +231 -133
  46. package/{index-xwprn9nr.js → index-nz15k0b6.js} +1 -1
  47. package/{index-nwd6qzvv.js → index-p9ystjrp.js} +138 -55
  48. package/index-q958tqqg.js +77 -0
  49. package/{index-50jjwg8y.js → index-qt22xstj.js} +1 -1
  50. package/{index-94s080qm.js → index-r2tpeb3n.js} +127 -1
  51. package/{index-bsv901fd.js → index-rwq3ekqc.js} +7 -7
  52. package/{index-t91a7339.js → index-tn4rpqp1.js} +96 -12
  53. package/{index-1gs9g12n.js → index-v29746vb.js} +4 -4
  54. package/index-ww9bhp67.js +62 -0
  55. package/{index-hxnnfy57.js → index-y8q31011.js} +180 -8
  56. package/index.js +22 -21
  57. package/{library.command-9m2rdwwz.js → library.command-gaga5bmq.js} +3 -3
  58. package/{localRegistry.command-kda81jyk.js → localRegistry.command-9e84e32q.js} +9 -9
  59. package/{module.command-q63dffya.js → module.command-7vpf6db4.js} +5 -5
  60. package/{package.command-y2jh0m99.js → package.command-twmvvf69.js} +3 -3
  61. package/package.json +2 -2
  62. package/{page.command-f3e4cwhh.js → page.command-5asq7fdf.js} +3 -3
  63. package/pipeline.command-d0v38572.js +1334 -0
  64. package/{primitive.command-r4kq75qj.js → primitive.command-dyrs2fm8.js} +6 -6
  65. package/projectTypecheck-cvtkrzp2.js +7 -0
  66. package/{quality.command-f7gzh26z.js → quality.command-6emsqpbg.js} +5 -4
  67. package/{repair.command-0qmyr6qa.js → repair.command-8t6aegv5.js} +5 -5
  68. package/{scalar.command-kddn7av4.js → scalar.command-bnggy1x7.js} +4 -4
  69. package/{subspace.command-ft65qvc4.js → subspace.command-f02j5tge.js} +158 -91
  70. package/templates/appSample/page/task/[taskId]/_index.tsx +2 -2
  71. package/templates/appSample/page/task/[taskId]/edit.tsx +2 -2
  72. package/templates/appSample/page/task/_index.tsx +2 -2
  73. package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -2
  74. package/templates/crudPages/[__model__Id]/page.tsx +3 -3
  75. package/templates/crudPages/page.tsx +2 -2
  76. package/templates/crudSinglePage/page.tsx +3 -3
  77. package/templates/lib/cnst.ts +2 -2
  78. package/templates/lib/sig.ts +3 -3
  79. package/{tunnel.command-b3ymdk4f.js → tunnel.command-9x8nfn75.js} +16 -13
  80. package/{workflow.command-z1srcdfw.js → workflow.command-jx0wag5r.js} +10 -10
  81. package/{workspace.command-vp6vmx3z.js → workspace.command-cmj6mqyk.js} +57 -27
  82. package/cloud.command-tw06613r.js +0 -95
  83. package/cloud.script-64gzba8r.js +0 -20
  84. package/context.runner-hnkvv3z3.js +0 -21
@@ -1,86 +1,17 @@
1
1
  // @bun
2
2
  import {
3
3
  binPlatforms,
4
+ ServerEnvProfile,
4
5
  Executor
5
- } from "./index-50wb2a26.js";
6
-
7
- // pkgs/@akanjs/devkit/mobile/mobileTarget.ts
8
- var trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
9
- var getMobileTargets = async (app) => {
10
- const config = await app.getConfig();
11
- return Object.entries(config.native.targets).map(([name, target]) => ({ name, config: target }));
12
- };
13
- var isTemplateOnly = ({ basePaths, native }) => basePaths.size > 0 && Object.values(native.targets).every((target) => !target.basePath);
14
- var getMobileTargetChoices = async (app) => {
15
- const config = await app.getConfig();
16
- const targetNames = Object.keys(config.native.targets);
17
- if (targetNames.length > 0 && !isTemplateOnly(config))
18
- return targetNames;
19
- return [...config.basePaths];
20
- };
21
- var resolveMobileTargetByBasePath = (targets, basePath) => {
22
- const normalizedBasePath = trimSlashes(basePath);
23
- const byBasePath = targets.find(({ config }) => config.basePath !== undefined && trimSlashes(config.basePath) === normalizedBasePath);
24
- if (byBasePath)
25
- return byBasePath;
26
- const [template] = targets;
27
- if (!template)
28
- return;
29
- return {
30
- name: normalizedBasePath,
31
- config: { ...template.config, name: normalizedBasePath, basePath: normalizedBasePath }
32
- };
33
- };
34
- var resolveMobileTargets = async (app, selection) => {
35
- const config = await app.getConfig();
36
- const targets = await getMobileTargets(app);
37
- if (targets.length === 0)
38
- throw new Error(`No native targets configured for ${app.name}`);
39
- if (!selection && targets.length === 1 && !isTemplateOnly(config))
40
- return targets;
41
- if (!selection) {
42
- const choices = await getMobileTargetChoices(app);
43
- if (choices.length === 1)
44
- return resolveMobileTargets(app, choices[0]);
45
- throw new Error(`Multiple native targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
46
- }
47
- if (selection === "all") {
48
- if (Object.keys(config.native.targets).length > 1)
49
- return targets;
50
- const basePaths = [...config.basePaths];
51
- if (basePaths.length > 1 || isTemplateOnly(config)) {
52
- return basePaths.flatMap((basePath) => {
53
- const resolved = resolveMobileTargetByBasePath(targets, basePath);
54
- return resolved ? [resolved] : [];
55
- });
56
- }
57
- return targets;
58
- }
59
- const target = targets.find((candidate) => candidate.name === selection);
60
- if (target)
61
- return [target];
62
- const basePathTarget = resolveMobileTargetByBasePath(targets, selection);
63
- if (basePathTarget && config.basePaths.has(trimSlashes(selection)))
64
- return [basePathTarget];
65
- const choices = await getMobileTargetChoices(app);
66
- throw new Error(`Native target '${selection}' was not found. Available: ${choices.join(", ")}`);
67
- };
68
- var resolveAppId = (appId, platform) => {
69
- if (typeof appId === "string")
70
- return appId;
71
- const id = appId[platform] ?? appId.default;
72
- if (!id)
73
- throw new Error(`native.appId names no id for ${platform}; give it ${platform} or default.`);
74
- return id;
75
- };
76
- var appIdsOf = (appId) => typeof appId === "string" ? [appId] : Object.values(appId).filter((id) => !!id);
77
- var targetHtmlFilename = (target) => {
78
- const basePath = trimSlashes(target.basePath ?? "");
79
- return basePath ? `${basePath}.html` : "index.html";
80
- };
6
+ } from "./index-55mynnqs.js";
7
+ import {
8
+ resolveAppId,
9
+ targetHtmlFilename
10
+ } from "./index-q958tqqg.js";
81
11
 
82
12
  // pkgs/@akanjs/devkit/mobile/nativeApp.ts
83
- import path6 from "path";
13
+ import { rm as rm3 } from "fs/promises";
14
+ import path7 from "path";
84
15
 
85
16
  // pkgs/@akanjs/devkit/mobile/desktopBin.ts
86
17
  import { chmod, mkdir, rename, rm, writeFile } from "fs/promises";
@@ -417,11 +348,19 @@ class NativeConfig {
417
348
  const desktop = NativeConfig.#compact({
418
349
  recovery: target.desktop?.recovery,
419
350
  screenCapture: target.desktop?.screenCapture,
351
+ webview2: target.desktop?.webview2,
352
+ publisher: target.desktop?.publisher,
420
353
  window: NativeConfig.#compact({ ...target.desktop?.window }),
421
354
  server: desktopServer,
422
355
  bin: desktopBin,
423
356
  dmg: NativeConfig.#dmg(target.desktop?.dmg, abs)
424
357
  });
358
+ const background = target.backgroundColor;
359
+ const shell = NativeConfig.#compact({
360
+ backgroundColor: typeof background === "string" ? background : background?.light,
361
+ backgroundColorDark: typeof background === "string" ? undefined : background?.dark,
362
+ devtools: target.devtools ? true : undefined
363
+ });
425
364
  const config = {
426
365
  app: {
427
366
  id: resolveAppId(target.appId, platform),
@@ -457,7 +396,8 @@ class NativeConfig {
457
396
  keyboard: { resize: "none" },
458
397
  ...desktop ? { desktop } : {},
459
398
  ...target.icon ? { icon: NativeConfig.#icon(target.icon, abs) } : {},
460
- ...target.splash ? { splash: NativeConfig.#splash(target.splash, abs) } : {}
399
+ ...target.splash ? { splash: NativeConfig.#splash(target.splash, abs) } : {},
400
+ ...shell ? { shell } : {}
461
401
  };
462
402
  return { config, warnings };
463
403
  }
@@ -480,8 +420,9 @@ class NativeConfig {
480
420
  };
481
421
  }
482
422
  static #updates(target, env) {
483
- const { url, publicKey, channel = env, ...rest } = target.updates ?? {};
484
- return url && publicKey ? { ...rest, url, publicKey, ...channel ? { channel } : {} } : undefined;
423
+ const { url, publicKey, publicKeys, channel = env, ...rest } = target.updates ?? {};
424
+ const keys = publicKeys ? { publicKeys: [...publicKeys] } : publicKey ? { publicKey } : null;
425
+ return url && keys ? { ...rest, url, ...keys, ...channel ? { channel } : {} } : undefined;
485
426
  }
486
427
  static fileNameOf(name) {
487
428
  return name.replace(/[^A-Za-z0-9._-]/g, "-").replace(/^[-.]+|[-.]+$/g, "") || "app";
@@ -614,6 +555,71 @@ ${script}${html.slice(at)}`;
614
555
  }
615
556
  }
616
557
 
558
+ // pkgs/@akanjs/devkit/mobile/serverBundleSecrets.ts
559
+ import { readdir as readdir2, readFile, stat as stat2 } from "fs/promises";
560
+ import path6 from "path";
561
+ class ServerBundleSecrets {
562
+ workspaceRoot;
563
+ appDir;
564
+ static #largestFile = 64 * 1024 * 1024;
565
+ constructor(workspaceRoot, appDir) {
566
+ this.workspaceRoot = workspaceRoot;
567
+ this.appDir = appDir;
568
+ }
569
+ async envFiles() {
570
+ const libsDir = path6.join(this.workspaceRoot, "libs");
571
+ const libs = await readdir2(libsDir).catch(() => []);
572
+ const dirs = [path6.join(this.appDir, "env"), ...libs.map((lib) => path6.join(libsDir, lib, "env"))];
573
+ const files = await Promise.all(dirs.map(async (dir) => (await readdir2(dir).catch(() => [])).filter((name) => /^env\.server\.[A-Za-z0-9.-]+\.ts$/.test(name) && !/\.(type|example)\.ts$/.test(name)).map((name) => path6.join(dir, name))));
574
+ return files.flat();
575
+ }
576
+ async assertClean(bundleDir) {
577
+ const unread = [];
578
+ const secrets = [];
579
+ for (const file of await this.envFiles()) {
580
+ try {
581
+ const value = await ServerEnvProfile.evaluate(file, path6.dirname(path6.dirname(file)));
582
+ secrets.push(...ServerEnvProfile.secretValues(value).map((secret) => ({ file, ...secret })));
583
+ } catch {
584
+ unread.push(ServerBundleSecrets.#relative(this.workspaceRoot, file));
585
+ }
586
+ }
587
+ const found = [];
588
+ for (const bundled of await ServerBundleSecrets.#files(bundleDir)) {
589
+ const text = await readFile(bundled, "latin1");
590
+ for (const secret of secrets)
591
+ if (ServerBundleSecrets.#needles(secret.value).some((needle) => text.includes(needle)))
592
+ found.push(`${ServerBundleSecrets.#relative(bundleDir, bundled)} holds ${ServerBundleSecrets.#relative(this.workspaceRoot, secret.file)} ${secret.path}`);
593
+ }
594
+ if (found.length)
595
+ throw new Error(`The server this app carries holds secret values from the workspace's env files, so it is not signed or sent anywhere:
596
+ ${[...new Set(found)].join(`
597
+ `)}
598
+ Build its server with a profile (native.desktop.server.profile), whose bundle carries no secret.`);
599
+ return unread;
600
+ }
601
+ static #needles(value) {
602
+ const escaped = JSON.stringify(value).slice(1, -1);
603
+ const line = value.split(/\r?\n/).find((part) => part.length >= 32);
604
+ return [...new Set([value, escaped, ...line ? [line] : []])];
605
+ }
606
+ static #relative(from, to) {
607
+ return path6.relative(from, to).split(path6.sep).join("/");
608
+ }
609
+ static async#files(dir) {
610
+ const files = [];
611
+ for (const entry of await readdir2(dir, { withFileTypes: true })) {
612
+ const file = path6.join(dir, entry.name);
613
+ if (entry.isDirectory()) {
614
+ if (entry.name !== "node_modules")
615
+ files.push(...await ServerBundleSecrets.#files(file));
616
+ } else if (entry.isFile() && (await stat2(file)).size <= ServerBundleSecrets.#largestFile)
617
+ files.push(file);
618
+ }
619
+ return files;
620
+ }
621
+ }
622
+
617
623
  // pkgs/@akanjs/devkit/mobile/nativeApp.ts
618
624
  class NativeApp {
619
625
  app;
@@ -626,14 +632,14 @@ class NativeApp {
626
632
  this.app = app;
627
633
  this.target = target;
628
634
  this.env = env;
629
- this.targetRoot = path6.join(app.workspace.workspaceRoot, "dist", "native", app.name, target.name);
630
- this.web = new NativeWebDir(path6.join(this.targetRoot, "web"));
635
+ this.targetRoot = path7.join(app.workspace.workspaceRoot, "dist", "native", app.name, target.name);
636
+ this.web = new NativeWebDir(path7.join(this.targetRoot, "web"));
631
637
  }
632
638
  outDir(platform) {
633
- return path6.join(this.targetRoot, "build", platform);
639
+ return path7.join(this.targetRoot, "build", platform);
634
640
  }
635
641
  devOutDir(platform) {
636
- return path6.join(this.targetRoot, "dev", platform);
642
+ return path7.join(this.targetRoot, "dev", platform);
637
643
  }
638
644
  indexPath(platform) {
639
645
  const { config } = this.target;
@@ -643,13 +649,13 @@ class NativeApp {
643
649
  async assembleWeb(platform) {
644
650
  const dist = this.app.dist.cwdPath;
645
651
  return await this.web.assemble({ ...this.target.config, indexPath: this.indexPath(platform) }, {
646
- html: path6.join(dist, "csr", targetHtmlFilename(this.target.config)),
647
- publicDir: path6.join(dist, "public"),
648
- fontsDir: path6.join(dist, ".akan", "artifact", "fonts")
652
+ html: path7.join(dist, "csr", targetHtmlFilename(this.target.config)),
653
+ publicDir: path7.join(dist, "public"),
654
+ fontsDir: path7.join(dist, ".akan", "artifact", "fonts")
649
655
  });
650
656
  }
651
657
  get binDir() {
652
- return path6.join(this.targetRoot, "bin");
658
+ return path7.join(this.targetRoot, "bin");
653
659
  }
654
660
  async config({
655
661
  server,
@@ -729,27 +735,18 @@ class NativeApp {
729
735
  return {};
730
736
  return { PUBLIC_AKAN_SERVER_URL: `http://localhost:${await this.app.getDevPort()}` };
731
737
  }
732
- async build(platform, { profile = "release", server, installer = false, arch } = {}) {
738
+ async build(platform, { profile = "release", server, installer = false, arch, sign = false, notarize = false } = {}) {
733
739
  NativeApp.#assertServerPlatform(platform, server);
734
740
  NativeApp.assertInstaller(platform, installer);
735
741
  await this.assembleWeb(platform);
736
742
  const { api, config } = await this.prepare(platform, server, arch);
743
+ const distribution = await this.#distribution(api, platform, profile, { sign, notarize }, server);
737
744
  return await api.build({
738
745
  ...this.#task(platform, config, { env: await this.#runtimeEnv(server) }),
739
746
  profile,
740
747
  ...arch ? { arch } : {},
741
- ...platform === "windows" ? {
742
- windows: {
743
- ...installer ? { installer } : {},
744
- ...profile === "release" ? NativeApp.#windowsSigning(api) : {}
745
- }
746
- } : {},
747
- ...platform === "macos" ? {
748
- macos: {
749
- ...profile === "release" ? api.macosDistributionFromEnv(process.env) : {},
750
- ...installer ? { dmg: true } : {}
751
- }
752
- } : {},
748
+ ...platform === "windows" ? { windows: { ...installer ? { installer } : {}, ...distribution.windows } } : {},
749
+ ...platform === "macos" ? { macos: { ...distribution.macos, ...installer ? { dmg: true } : {} } } : {},
753
750
  ...platform === "linux" && installer ? { linux: { appImage: true } } : {}
754
751
  });
755
752
  }
@@ -815,23 +812,46 @@ class NativeApp {
815
812
  });
816
813
  }
817
814
  get updatesDir() {
818
- return path6.join(this.targetRoot, "updates");
815
+ return path7.join(this.targetRoot, "updates");
819
816
  }
820
- async publishUpdate(platform, { channel, server } = {}) {
817
+ async publishUpdate(platform, {
818
+ channel,
819
+ server,
820
+ arch,
821
+ sign = false,
822
+ notarize = false
823
+ } = {}) {
821
824
  NativeApp.#assertServerPlatform(platform, server);
822
825
  this.#assertUpdates();
823
826
  await this.assembleWeb(platform);
824
- const { api, config } = await this.prepare(platform, server);
827
+ const { api, config } = await this.prepare(platform, server, arch);
828
+ const distribution = await this.#distribution(api, platform, "release", { sign, notarize }, server);
825
829
  return await api.publishUpdate({
826
830
  ...this.#task(platform, config, { env: await this.#runtimeEnv(server) }),
827
831
  platform,
828
832
  out: this.updatesDir,
829
833
  ...channel ? { channel } : {},
830
- ...platform === "macos" ? { macos: api.macosDistributionFromEnv(process.env) } : {},
831
- ...platform === "windows" ? { windows: NativeApp.#windowsSigning(api) } : {}
834
+ ...arch ? { arch } : {},
835
+ ...distribution.macos ? { macos: distribution.macos } : {},
836
+ ...distribution.windows ? { windows: distribution.windows } : {}
832
837
  });
833
838
  }
834
- async assertPublishable(platform, { channel, server = false } = {}) {
839
+ async#distribution(api, platform, profile, request, server) {
840
+ const distribution = api.requestedDistribution(platform, profile, request);
841
+ if (!distribution.notice) {
842
+ if (distribution.unused.length && (platform === "macos" || platform === "windows"))
843
+ this.app.logger.info(`${distribution.unused.join(", ")} set: this build is not signed without --sign.`);
844
+ return distribution;
845
+ }
846
+ if (server) {
847
+ const unread = await new ServerBundleSecrets(this.app.workspace.workspaceRoot, this.app.cwdPath).assertClean(server.dir);
848
+ if (unread.length)
849
+ this.app.logger.warn(`Not searched for secrets, as they could not be evaluated: ${unread.join(", ")}.`);
850
+ }
851
+ this.app.logger.warn(distribution.notice);
852
+ return distribution;
853
+ }
854
+ async assertPublishable(platform, { channel, server = false, arch } = {}) {
835
855
  this.#assertUpdates();
836
856
  const [api, { config }] = await Promise.all([
837
857
  NativeApi.load(this.app.cwdPath),
@@ -845,7 +865,8 @@ class NativeApp {
845
865
  platform,
846
866
  out: this.updatesDir,
847
867
  server,
848
- ...channel ? { channel } : {}
868
+ ...channel ? { channel } : {},
869
+ ...arch ? { arch } : {}
849
870
  });
850
871
  } catch (error) {
851
872
  throw new Error(`Native target '${this.target.name}': ${error instanceof Error ? error.message : String(error)}`, {
@@ -880,12 +901,31 @@ class NativeApp {
880
901
  const { api, config } = await this.prepare(platform);
881
902
  if (!config.updates)
882
903
  throw new Error(`Native target '${this.target.name}' takes no updates: add native.updates: { url, publicKey } to akan.config.ts first.`);
883
- const packed = await api.packUpdate({
884
- ...this.#task(platform, config),
904
+ const dir = path7.resolve(out ?? path7.join(this.updatesDir, platform));
905
+ await Promise.all(["pack.json", "compat.json"].map((name) => rm3(path7.join(dir, name), { force: true })));
906
+ const packed = await api.packUpdate({ ...this.#task(platform, config), platform, out: dir });
907
+ const channel = config.updates.channel ?? "production";
908
+ const bundle = await Bun.file(path7.join(packed.out, "bundle.json")).json();
909
+ const pack = {
910
+ schema: 1,
911
+ app: this.app.name,
912
+ target: this.target.name,
885
913
  platform,
886
- out: out ?? path6.join(this.updatesDir, platform)
887
- });
888
- return { ...packed, channel: config.updates.channel ?? "production" };
914
+ env: this.env ?? "main",
915
+ channel,
916
+ appId: packed.manifest.app,
917
+ appVersion: packed.manifest.version,
918
+ nativeApi: packed.manifest.nativeApi ?? "",
919
+ webHash: bundle.web.hash,
920
+ files: packed.manifest.files.length,
921
+ size: packed.manifest.files.reduce((sum, file) => sum + file.size, 0),
922
+ out: packed.out
923
+ };
924
+ return { ...packed, channel, pack };
925
+ }
926
+ static async writePack(pack) {
927
+ await Bun.write(path7.join(pack.out, "pack.json"), `${JSON.stringify(pack, null, 2)}
928
+ `);
889
929
  }
890
930
  static async compareBundles(appDir, shipped, bundle) {
891
931
  return (await NativeApi.load(appDir)).compareBundles(shipped, bundle);
@@ -916,10 +956,6 @@ class NativeApp {
916
956
  const home = [lang, basePath, indexPath].flatMap((part) => (part ?? "").split("/")).filter((segment) => segment.length > 0);
917
957
  return `/${home.join("/")}?${params}`;
918
958
  }
919
- static #windowsSigning(api) {
920
- const signing = api.windowsSigningFromEnv(process.env);
921
- return signing ? { signing } : {};
922
- }
923
959
  static assertInstaller(platform, installer) {
924
960
  if (installer && (platform === "ios" || platform === "android"))
925
961
  throw new Error(`An installer is built for a desktop app (Windows, macOS, Linux), not for ${platform}.`);
@@ -962,8 +998,8 @@ var isPlaceholderAppId = (appId) => {
962
998
  return normalized.split(".").some((segment) => placeholderAppIdSegment.test(segment));
963
999
  };
964
1000
  // pkgs/@akanjs/devkit/mobile/desktopServerStage.ts
965
- import { cp as cp2, mkdir as mkdir3, readdir as readdir2, rm as rm3, writeFile as writeFile2 } from "fs/promises";
966
- import path7 from "path";
1001
+ import { cp as cp2, mkdir as mkdir3, readdir as readdir3, rm as rm4, writeFile as writeFile2 } from "fs/promises";
1002
+ import path8 from "path";
967
1003
  class DesktopServerStage {
968
1004
  app;
969
1005
  static uncarried = new Set([
@@ -980,23 +1016,25 @@ class DesktopServerStage {
980
1016
  dir;
981
1017
  constructor(app) {
982
1018
  this.app = app;
983
- this.dir = path7.join(app.cwdPath, ".akan", "desktop", "server");
1019
+ this.dir = path8.join(app.cwdPath, ".akan", "desktop", "server");
984
1020
  }
985
1021
  static assertCarriable(config) {
986
1022
  if (!config.database.modes.includes("single"))
987
1023
  throw new Error(`desktop.server in native puts the server in the app, where only database mode single runs (no Redis or Postgres); apps/${config.app.name}/akan.config.ts declares ${config.database.modes.join(", ")}.`);
988
1024
  }
989
- static env(config, environment) {
1025
+ static env(config, environment, { modules = [], env = {} } = {}) {
990
1026
  const { PORT: _port, AKAN_LOG_TO_FILE: _fileLog, ...image } = config.getProductionEnv(environment);
991
1027
  return {
992
1028
  ...image,
1029
+ ...env,
993
1030
  AKAN_PUBLIC_OPERATION_MODE: "edge",
994
1031
  AKAN_DATABASE_MODE: "single",
995
1032
  AKAN_DATABASE_MODES: "single",
996
1033
  AKAN_SSR: "false",
997
1034
  AKAN_CSR: "false",
998
1035
  AKAN_MCP: "false",
999
- AKAN_SHUTDOWN_TIMEOUT_MS: "1000"
1036
+ AKAN_SHUTDOWN_TIMEOUT_MS: "1000",
1037
+ ...modules.length ? { AKAN_MODULES: modules.join(",") } : {}
1000
1038
  };
1001
1039
  }
1002
1040
  static imageStepsNotice(config, carried) {
@@ -1020,7 +1058,7 @@ class DesktopServerStage {
1020
1058
  static async dependentsOf(nodeModules, name) {
1021
1059
  const dependents = new Set;
1022
1060
  for await (const manifest of new Bun.Glob("**/package.json").scan({ cwd: nodeModules, onlyFiles: true })) {
1023
- const json = await Bun.file(path7.join(nodeModules, manifest)).json().catch(() => null);
1061
+ const json = await Bun.file(path8.join(nodeModules, manifest)).json().catch(() => null);
1024
1062
  if (!json?.name || json.name === name)
1025
1063
  continue;
1026
1064
  const declared = { ...json.dependencies, ...json.optionalDependencies, ...json.peerDependencies };
@@ -1035,26 +1073,26 @@ class DesktopServerStage {
1035
1073
  async install(arch, omit = []) {
1036
1074
  const config = await this.app.getConfig();
1037
1075
  DesktopServerStage.assertCarriable(config);
1038
- await rm3(this.dir, { recursive: true, force: true });
1076
+ await rm4(this.dir, { recursive: true, force: true });
1039
1077
  await mkdir3(this.dir, { recursive: true });
1040
1078
  const built = config.getProductionPackageJson();
1041
1079
  for (const name of omit.filter((name) => !(name in (built.dependencies ?? {}))))
1042
1080
  this.app.logger.warn(`desktop.server.omit names ${name}, which the server does not depend on directly.`);
1043
- await writeFile2(path7.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, built, omit), null, 2));
1081
+ await writeFile2(path8.join(this.dir, "package.json"), JSON.stringify(DesktopServerStage.packageJson(config, built, omit), null, 2));
1044
1082
  try {
1045
1083
  await this.app.spawn(process.execPath, DesktopServerStage.installArgs(arch), { cwd: this.dir });
1046
1084
  } catch (error) {
1047
1085
  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.
1048
1086
  ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1049
1087
  }
1050
- await rm3(path7.join(this.dir, "node_modules", ".bin"), { recursive: true, force: true });
1088
+ await rm4(path8.join(this.dir, "node_modules", ".bin"), { recursive: true, force: true });
1051
1089
  await this.#assertOmitted(omit);
1052
1090
  }
1053
1091
  async#assertOmitted(omit) {
1054
- const nodeModules = path7.join(this.dir, "node_modules");
1092
+ const nodeModules = path8.join(this.dir, "node_modules");
1055
1093
  const kept = [];
1056
1094
  for (const name of omit) {
1057
- if (!await Bun.file(path7.join(nodeModules, name, "package.json")).exists())
1095
+ if (!await Bun.file(path8.join(nodeModules, name, "package.json")).exists())
1058
1096
  continue;
1059
1097
  const dependents = await DesktopServerStage.dependentsOf(nodeModules, name);
1060
1098
  kept.push(`${name} (needed by ${dependents.join(", ") || "a package that names it"})`);
@@ -1062,19 +1100,79 @@ ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1062
1100
  if (kept.length)
1063
1101
  throw new Error(`desktop.server.omit leaves out packages another dependency still installs: ${kept.join("; ")}. Omit those too, or keep the package.`);
1064
1102
  }
1065
- async prepare(environment) {
1103
+ async prepare(environment, run = {}) {
1066
1104
  const config = await this.app.getConfig();
1067
1105
  const dist = this.app.dist.cwdPath;
1068
1106
  await mkdir3(this.dir, { recursive: true });
1069
- for (const name of await readdir2(dist))
1107
+ for (const name of await readdir3(dist))
1070
1108
  if (!DesktopServerStage.uncarried.has(name))
1071
- await cp2(path7.join(dist, name), path7.join(this.dir, name), { recursive: true });
1109
+ await cp2(path8.join(dist, name), path8.join(this.dir, name), { recursive: true });
1072
1110
  const scanInfo = this.app.getScanInfo({ allowEmpty: true }) ?? await this.app.scan({ write: false });
1073
1111
  const carried = [...DesktopBin.select(config, scanInfo.getLibs()).keys()];
1074
1112
  const notice = DesktopServerStage.imageStepsNotice(config, carried);
1075
1113
  if (notice)
1076
1114
  this.app.logger[notice.level](notice.message);
1077
- return { dir: this.dir, entry: "main.js", env: DesktopServerStage.env(config, environment) };
1115
+ return {
1116
+ dir: this.dir,
1117
+ entry: "main.js",
1118
+ env: DesktopServerStage.env(config, environment, run),
1119
+ ...run.restart !== undefined ? { restart: run.restart } : {},
1120
+ ...run.watchdog ? { watchdog: run.watchdog } : {}
1121
+ };
1122
+ }
1123
+ }
1124
+ // pkgs/@akanjs/devkit/mobile/updateUploader.ts
1125
+ import path9 from "path";
1126
+
1127
+ class UpdateUploader {
1128
+ bucket;
1129
+ prefix;
1130
+ static #manifest = /\.json(?:\.sig)?$/;
1131
+ static parseTarget(target) {
1132
+ const match = /^s3:\/\/([^/]+)\/?(.*)$/.exec(target.trim());
1133
+ if (!match?.[1])
1134
+ throw new Error(`--upload takes s3://<bucket>/<prefix>, not ${JSON.stringify(target)}.`);
1135
+ return { bucket: match[1], prefix: (match[2] ?? "").replace(/\/+$/, "") };
1136
+ }
1137
+ static order(files) {
1138
+ const manifests = files.filter((file) => UpdateUploader.#manifest.test(file));
1139
+ return {
1140
+ releases: files.filter((file) => !UpdateUploader.#manifest.test(file)).sort((a, b) => a.localeCompare(b)),
1141
+ manifests: manifests.sort((a, b) => Number(a.endsWith(".json")) - Number(b.endsWith(".json")) || a.localeCompare(b))
1142
+ };
1143
+ }
1144
+ constructor(bucket, prefix) {
1145
+ this.bucket = bucket;
1146
+ this.prefix = prefix;
1147
+ }
1148
+ static forTarget(target) {
1149
+ const { bucket, prefix } = UpdateUploader.parseTarget(target);
1150
+ return new UpdateUploader(new Bun.S3Client({ bucket }), prefix);
1151
+ }
1152
+ async upload(dir) {
1153
+ const files = [];
1154
+ for await (const file of new Bun.Glob("**/*").scan({ cwd: dir, onlyFiles: true })) {
1155
+ if (!file.endsWith(".tmp"))
1156
+ files.push(file.split(path9.sep).join("/"));
1157
+ }
1158
+ const { releases, manifests } = UpdateUploader.order(files);
1159
+ const keyOf = (file) => this.prefix ? `${this.prefix}/${file}` : file;
1160
+ let uploaded = 0;
1161
+ let skipped = 0;
1162
+ for (const file of releases) {
1163
+ if (await this.bucket.exists(keyOf(file))) {
1164
+ skipped++;
1165
+ continue;
1166
+ }
1167
+ await this.bucket.write(keyOf(file), Bun.file(path9.join(dir, file)));
1168
+ uploaded++;
1169
+ }
1170
+ for (const file of manifests) {
1171
+ const type = file.endsWith(".json") ? "application/json" : "text/plain";
1172
+ await this.bucket.write(keyOf(file), Bun.file(path9.join(dir, file)), { type });
1173
+ uploaded++;
1174
+ }
1175
+ return { uploaded, skipped };
1078
1176
  }
1079
1177
  }
1080
- export { DesktopServerStage, getMobileTargets, getMobileTargetChoices, resolveMobileTargets, resolveAppId, appIdsOf, NativeApp, isPlaceholderAppId };
1178
+ export { DesktopServerStage, NativeApp, isPlaceholderAppId, UpdateUploader };
@@ -116,7 +116,7 @@ class SubagentPool2 {
116
116
  }
117
117
  }
118
118
  async#run(id, kind, prompt, signal) {
119
- const { CodeAgent } = await import("./CodeAgent-szbjv4gv.js");
119
+ const { CodeAgent } = await import("./CodeAgent-q7a4srt6.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({