@akanjs/cli 3.0.2 → 3.1.0-rc.1

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 (87) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-szbjv4gv.js → CodeAgent-kyprmy06.js} +2 -2
  3. package/{CodeTui-5nw86ab6.js → CodeTui-21mtze3j.js} +1 -1
  4. package/{TunnelShare-cwh9ezsm.js → TunnelShare-q4jq08qg.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-5s780vnz.js} +37 -15
  8. package/{applicationBuildRunner-wx09n9g6.js → applicationBuildRunner-1yaqsrdq.js} +115 -103
  9. package/buildBatch.proc.js +16 -5
  10. package/cloud.command-yj6xb2a7.js +192 -0
  11. package/cloud.script-h6pvcpa2.js +20 -0
  12. package/{code.command-hmtx14b2.js → code.command-3wkn4ja5.js} +6 -6
  13. package/commandManifest.json +1 -1
  14. package/{context.command-6aqvdx43.js → context.command-9jjn3gcw.js} +13 -13
  15. package/context.runner-atcrzz5b.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 +70 -25
  19. package/guidelines/cssRule/cssRule.instruction.md +19 -0
  20. package/guidelines/mcpRule/mcpRule.instruction.md +72 -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-jk9xnck6.js → index-1w2qaee2.js} +240 -133
  29. package/{index-g38nhv87.js → index-2etcv0sc.js} +1 -1
  30. package/{index-zyw622y1.js → index-2mx692qq.js} +3 -3
  31. package/{index-50wb2a26.js → index-55mynnqs.js} +354 -129
  32. package/{index-2wc41k5w.js → index-56d2g8ms.js} +2 -2
  33. package/{index-q6ft9fkc.js → index-6jjtnhkw.js} +8 -3
  34. package/index-6w0rbr9d.js +68 -0
  35. package/{index-hxnnfy57.js → index-8ggztp8k.js} +204 -8
  36. package/{index-zwd84m0w.js → index-8r4d7cmt.js} +4 -4
  37. package/{index-j38qkvkk.js → index-ae5pwhgs.js} +12 -10
  38. package/{index-gtjgnv36.js → index-aprt7pkz.js} +6 -9
  39. package/{index-1bjkfvm9.js → index-c1x0be5j.js} +2 -2
  40. package/{index-z750gk1j.js → index-c2yrsxdh.js} +13 -7
  41. package/{index-cb6yc5mb.js → index-c76k723e.js} +2 -2
  42. package/{index-6keyf4sg.js → index-ctanya41.js} +4 -4
  43. package/{index-f4e08ptp.js → index-e0cvze1s.js} +4 -4
  44. package/{index-hp3stk1m.js → index-fm46xrf1.js} +3 -3
  45. package/{index-3xmzr1p1.js → index-gnyf1fqy.js} +2 -2
  46. package/{index-nwd6qzvv.js → index-jx4bzvwg.js} +138 -55
  47. package/{index-jb0hmt3k.js → index-krq7btbt.js} +3 -3
  48. package/{index-94s080qm.js → index-pgj2rt78.js} +139 -1
  49. package/index-q958tqqg.js +77 -0
  50. package/{index-50jjwg8y.js → index-qt22xstj.js} +1 -1
  51. package/index-rc5rwyps.js +201 -0
  52. package/{index-xwprn9nr.js → index-rtct7d43.js} +1 -1
  53. package/{index-bsv901fd.js → index-rwq3ekqc.js} +7 -7
  54. package/{index-1gs9g12n.js → index-v29746vb.js} +4 -4
  55. package/index-ww9bhp67.js +62 -0
  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-vsreyy42.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-md2teygq.js} +158 -91
  70. package/templates/app/env/env.client.type.ts +2 -3
  71. package/templates/app/page/styles.css.template +8 -0
  72. package/templates/appSample/page/task/[taskId]/_index.tsx +2 -2
  73. package/templates/appSample/page/task/[taskId]/edit.tsx +2 -2
  74. package/templates/appSample/page/task/_index.tsx +2 -2
  75. package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -2
  76. package/templates/crudPages/[__model__Id]/page.tsx +3 -3
  77. package/templates/crudPages/page.tsx +2 -2
  78. package/templates/crudSinglePage/page.tsx +3 -3
  79. package/templates/lib/cnst.ts +2 -2
  80. package/templates/lib/sig.ts +3 -3
  81. package/{tunnel.command-b3ymdk4f.js → tunnel.command-jx3tdm2d.js} +16 -13
  82. package/{workflow.command-z1srcdfw.js → workflow.command-jx0wag5r.js} +10 -10
  83. package/{workspace.command-vp6vmx3z.js → workspace.command-yxd7rr7g.js} +57 -27
  84. package/cloud.command-tw06613r.js +0 -95
  85. package/cloud.script-64gzba8r.js +0 -20
  86. package/context.runner-hnkvv3z3.js +0 -21
  87. package/index-t91a7339.js +0 -104
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  GlobalConfig,
4
4
  CloudApi
5
- } from "./index-94s080qm.js";
5
+ } from "./index-pgj2rt78.js";
6
6
 
7
7
  // pkgs/@akanjs/cli/tunnel/TunnelShare.ts
8
8
  import { TunnelAgent } from "akanjs/server/tunnel";
@@ -18,7 +18,7 @@ class TunnelShare2 {
18
18
  this.grant = grant;
19
19
  }
20
20
  static async open(app, { workspace, host, port, ttlMinutes, onLost }) {
21
- const api = await CloudApi.fromHost(workspace, host ?? GlobalConfig.akanCloudHost);
21
+ const api = await CloudApi.fromHost(workspace, await GlobalConfig.hostFor(workspace, host));
22
22
  const grant = await api.requestTunnel({ name: app.name, ...ttlMinutes ? { ttlMinutes } : {} });
23
23
  const origin = `http://localhost:${port ?? await app.getDevPort()}`;
24
24
  const agent = new TunnelAgent({
@@ -10,7 +10,7 @@ import {
10
10
  LibExecutor,
11
11
  PkgExecutor,
12
12
  ModuleExecutor
13
- } from "./index-50wb2a26.js";
13
+ } from "./index-55mynnqs.js";
14
14
 
15
15
  // pkgs/@akanjs/devkit/commandDecorators/camelToKebabCase.ts
16
16
  var camelToKebabCase = (str) => str.replace(/([A-Z])/g, "-$1").toLowerCase();
@@ -537,9 +537,10 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
537
537
  throw new Error(`Invalid system type: ${argMeta.type}`);
538
538
  };
539
539
  var runCommands = async (...commands) => {
540
+ let failureExitCode = 1;
540
541
  process.on("unhandledRejection", (error) => {
541
542
  printCliError(error);
542
- process.exit(1);
543
+ process.exit(failureExitCode);
543
544
  });
544
545
  const __dirname2 = getDirname2(import.meta.url);
545
546
  const packageJsonCandidates = [`${path2.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
@@ -576,6 +577,9 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
576
577
  let programCommand = program.command(commandName, {
577
578
  hidden: targetMeta.targetOption.devOnly
578
579
  });
580
+ const targetFailureExitCode = targetMeta.targetOption.failureExitCode;
581
+ if (targetFailureExitCode !== undefined)
582
+ programCommand.exitOverride((error) => process.exit(error.exitCode === 0 ? 0 : targetFailureExitCode));
579
583
  const [allArgMetas] = getArgMetas(command, targetMeta.key);
580
584
  for (const argMeta of allArgMetas) {
581
585
  if (argMeta.type === "Option")
@@ -598,6 +602,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
598
602
  return formatCommandHelp(command, targetMeta.key);
599
603
  };
600
604
  programCommand.action(async (...args) => {
605
+ failureExitCode = targetFailureExitCode ?? 1;
601
606
  if (!targetMeta.targetOption.stdio)
602
607
  Logger.rawLog();
603
608
  const cmdArgs = args.slice(0, args.length - 2);
@@ -642,7 +647,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
642
647
  }
643
648
  await program.parseAsync(process.argv).catch((error) => {
644
649
  printCliError(error);
645
- process.exit(1);
650
+ process.exit(failureExitCode);
646
651
  });
647
652
  };
648
653
 
@@ -0,0 +1,68 @@
1
+ // @bun
2
+ // pkgs/@akanjs/devkit/typecheck/projectTypecheck.ts
3
+ import { mkdir, rm } from "fs/promises";
4
+ import path from "path";
5
+
6
+ class ProjectTypecheck2 {
7
+ projectDir;
8
+ workspaceRoot;
9
+ env;
10
+ static #testIncludes = ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"];
11
+ static #excludes = [".akan/**", "node_modules/**", "local/**"];
12
+ constructor(projectDir, workspaceRoot, env) {
13
+ this.projectDir = projectDir;
14
+ this.workspaceRoot = workspaceRoot;
15
+ this.env = env;
16
+ }
17
+ get typecheckDir() {
18
+ return path.join(this.projectDir, ".akan", "typecheck");
19
+ }
20
+ async run(entries, { clean = false, incremental = true, tests = false } = {}) {
21
+ const name = tests ? "tsconfig.tests" : "tsconfig";
22
+ await mkdir(this.typecheckDir, { recursive: true });
23
+ const tsconfig = {
24
+ extends: "../../tsconfig.json",
25
+ compilerOptions: { noEmit: true, incremental, tsBuildInfoFile: `./${name}.tsbuildinfo` },
26
+ include: [...entries, ...tests ? ProjectTypecheck2.#testIncludes.map((glob) => `../../${glob}`) : []],
27
+ ...tests ? { exclude: ProjectTypecheck2.#excludes.map((glob) => `../../${glob}`) } : {},
28
+ references: []
29
+ };
30
+ const tsconfigPath = path.join(this.typecheckDir, `${name}.json`);
31
+ await Bun.write(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
32
+ `);
33
+ if (clean)
34
+ await rm(path.join(this.typecheckDir, `${name}.tsbuildinfo`), { force: true });
35
+ await this.#check(tsconfigPath);
36
+ return tsconfigPath;
37
+ }
38
+ async#check(tsconfigPath) {
39
+ const proc = Bun.spawn([process.execPath, await this.#workerEntry()], {
40
+ cwd: this.workspaceRoot,
41
+ env: { ...this.env, AKAN_COMMAND_TYPE: "typecheck", AKAN_TYPECHECK_TSCONFIG: tsconfigPath },
42
+ stdout: "pipe",
43
+ stderr: "pipe"
44
+ });
45
+ const [stdout, stderr, exitCode] = await Promise.all([
46
+ new Response(proc.stdout).text(),
47
+ new Response(proc.stderr).text(),
48
+ proc.exited
49
+ ]);
50
+ if (exitCode !== 0)
51
+ throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
52
+ }
53
+ async#workerEntry() {
54
+ const candidates = [
55
+ path.join(this.workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
56
+ path.join(this.workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
57
+ path.join(import.meta.dir, "typecheck.proc.js"),
58
+ path.join(import.meta.dir, "typecheck.proc.ts"),
59
+ path.join(import.meta.dir, "..", "typecheck.proc.js")
60
+ ];
61
+ for (const candidate of candidates)
62
+ if (await Bun.file(candidate).exists())
63
+ return candidate;
64
+ throw new Error(`[cli] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
65
+ }
66
+ }
67
+
68
+ export { ProjectTypecheck2 };
@@ -2,15 +2,25 @@
2
2
  import {
3
3
  getDefaultHostConfig,
4
4
  GlobalConfig,
5
- CloudApi
6
- } from "./index-94s080qm.js";
5
+ CloudApi,
6
+ UpdatePackUpload
7
+ } from "./index-pgj2rt78.js";
7
8
  import {
8
9
  runner
9
- } from "./index-q6ft9fkc.js";
10
+ } from "./index-6jjtnhkw.js";
10
11
  import {
11
12
  WorkspaceExecutor,
12
13
  AppExecutor
13
- } from "./index-50wb2a26.js";
14
+ } from "./index-55mynnqs.js";
15
+ import {
16
+ NativeApp
17
+ } from "./index-1w2qaee2.js";
18
+ import {
19
+ SubspaceConfig
20
+ } from "./index-ww9bhp67.js";
21
+ import {
22
+ resolveMobileTargets
23
+ } from "./index-q958tqqg.js";
14
24
  import {
15
25
  openBrowser
16
26
  } from "./index-9dp031by.js";
@@ -647,9 +657,11 @@ class PlatformTestRun {
647
657
  }
648
658
 
649
659
  // pkgs/@akanjs/cli/cloud/cloud.runner.ts
660
+ import { rm } from "fs/promises";
661
+ import os3 from "os";
650
662
  import path7 from "path";
651
- import { confirm, input, select } from "@inquirer/prompts";
652
- import { Logger, sleep } from "akanjs/common";
663
+ import { confirm, input, password, select } from "@inquirer/prompts";
664
+ import { Logger, PipelineNames, sleep } from "akanjs/common";
653
665
  import chalk2 from "chalk";
654
666
  import * as QRcode from "qrcode";
655
667
 
@@ -1001,14 +1013,18 @@ ${run.format(results)}
1001
1013
  }
1002
1014
  async downloadEnv(cloudApi, workspace, workspaceId) {
1003
1015
  await workspace.mkdir("local");
1004
- const localPath = await cloudApi.downloadEnv(workspaceId);
1016
+ const localPath = await cloudApi.downloadEnv(workspaceId).catch((error) => {
1017
+ throw CloudRunner.#refusal(error, "downloading its env archive takes the project owner's own `akan login`");
1018
+ });
1005
1019
  const relativePath = path7.relative(workspace.workspaceRoot, localPath).split(path7.sep).join("/");
1006
1020
  await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
1007
1021
  await workspace.remove(localPath);
1008
1022
  }
1009
1023
  async uploadEnv(cloudApi, workspaceId, filePath) {
1010
1024
  const file = new File([Bun.file(filePath)], path7.basename(filePath));
1011
- await cloudApi.uploadEnv(workspaceId, file);
1025
+ await cloudApi.uploadEnv(workspaceId, file).catch((error) => {
1026
+ throw CloudRunner.#refusal(error, "an env archive is uploaded by a project member signed in with `akan login`");
1027
+ });
1012
1028
  }
1013
1029
  async downloadEnvByScp(workspace) {
1014
1030
  const envArchivePath = "local/env.tar";
@@ -1080,6 +1096,186 @@ ${run.format(results)}
1080
1096
  }));
1081
1097
  return secretPaths.flat();
1082
1098
  }
1099
+ async secretProject(workspace, subspace) {
1100
+ if (!subspace) {
1101
+ const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
1102
+ if (!workspaceId)
1103
+ throw new Error("AKAN_WORKSPACE_ID is not set in the workspace .env, so there is no project to address.");
1104
+ return workspaceId;
1105
+ }
1106
+ const config = await SubspaceConfig.from(workspace);
1107
+ if (!config)
1108
+ throw new Error("The workspace root akan.config.ts declares no `subspace`.");
1109
+ const [declaration] = config.select([subspace]);
1110
+ if (!declaration?.workspaceId)
1111
+ throw new Error(`Subspace "${subspace}" declares no workspaceId in ${SubspaceConfig.source}.`);
1112
+ return declaration.workspaceId;
1113
+ }
1114
+ async readSecretValue(name, file) {
1115
+ const raw = file ? await Bun.file(file).text() : process.stdin.isTTY ? await password({ message: `Value of ${name} (hidden; it can never be read back)`, mask: "*" }) : await Bun.stdin.text();
1116
+ const value = raw.replace(/\r?\n$/, "");
1117
+ if (!value)
1118
+ throw new Error(`The value of ${name} is empty.`);
1119
+ const bytes = new TextEncoder().encode(value).length;
1120
+ if (bytes > CloudRunner.#secretBytes)
1121
+ throw new Error(`The value of ${name} is ${bytes} bytes, over ${CloudRunner.#secretBytes}.`);
1122
+ return value;
1123
+ }
1124
+ async uploadSecret(cloudApi, devProjectId, { name, branch, value }) {
1125
+ CloudRunner.#assertSecretName(name);
1126
+ CloudRunner.#assertSecretBranch(branch);
1127
+ const saved = await cloudApi.uploadSecret(devProjectId, { scope: branch ?? "*", name, value }).catch((error) => {
1128
+ throw CloudRunner.#refusal(error, "a secret is written by a project member signed in with `akan login`", {
1129
+ unavailable: CloudRunner.#secretsUnavailable
1130
+ });
1131
+ });
1132
+ Logger.rawLog(`Saved ${saved.name} for ${saved.scope === "*" ? "every branch" : `branch ${saved.scope}`}. Builds that start from now on use it.`);
1133
+ }
1134
+ async listSecrets(cloudApi, devProjectId, branch) {
1135
+ CloudRunner.#assertSecretBranch(branch);
1136
+ const listed = await cloudApi.listSecrets(devProjectId).catch((error) => {
1137
+ throw CloudRunner.#refusal(error, "the secrets are listed for a project member", {
1138
+ unavailable: CloudRunner.#secretsUnavailable
1139
+ });
1140
+ });
1141
+ const secrets = listed.filter((secret) => !branch || secret.scope === "*" || secret.scope === branch);
1142
+ Logger.rawLog(CloudRunner.formatSecrets(secrets));
1143
+ }
1144
+ async removeSecret(cloudApi, devProjectId, { name, branch }) {
1145
+ CloudRunner.#assertSecretName(name);
1146
+ CloudRunner.#assertSecretBranch(branch);
1147
+ const removed = await cloudApi.removeSecret(devProjectId, { scope: branch ?? "*", name }).catch((error) => {
1148
+ throw CloudRunner.#refusal(error, "a secret is removed by a project member signed in with `akan login`", {
1149
+ unavailable: CloudRunner.#secretsUnavailable
1150
+ });
1151
+ });
1152
+ Logger.rawLog(removed ? `Removed ${name} for ${branch ? `branch ${branch}` : "every branch"}.` : `No ${name} for ${branch ? `branch ${branch}` : "every branch"} to remove.`);
1153
+ }
1154
+ static #packRecords = ["manifest.template.json", "bundle.json", "pack.json", "compat.json"];
1155
+ static async#tarPack(pack) {
1156
+ const templateFile = Bun.file(path7.join(pack.out, "manifest.template.json"));
1157
+ if (!await templateFile.exists())
1158
+ throw new Error(`${pack.out} holds no manifest.template.json: give the folder \`akan pack-update\` wrote.`);
1159
+ const template = await templateFile.json();
1160
+ const hashes = new Set((template.files ?? []).map((file) => file.sha256));
1161
+ if (![...hashes].every((hash) => typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash)))
1162
+ throw new Error(`${pack.out}/manifest.template.json lists a file whose sha256 is not 64 hex digits.`);
1163
+ const present = await Promise.all(CloudRunner.#packRecords.map((name) => Bun.file(path7.join(pack.out, name)).exists()));
1164
+ const entries = [
1165
+ ...CloudRunner.#packRecords.filter((_, idx) => present[idx]),
1166
+ ...[...hashes].map((hash) => `files/${hash}`)
1167
+ ];
1168
+ const base = path7.join(os3.tmpdir(), `akan-pack-${pack.app}-${pack.target}-${pack.platform}-${process.pid}`);
1169
+ await Bun.write(`${base}.list`, entries.map((entry) => `./${entry}
1170
+ `).join(""));
1171
+ try {
1172
+ const tar = Bun.spawn(["tar", "-czf", `${base}.tgz`, "-C", pack.out, "-T", `${base}.list`], {
1173
+ stdout: "ignore",
1174
+ stderr: "pipe"
1175
+ });
1176
+ if (await tar.exited !== 0)
1177
+ throw new Error(`tar of ${pack.out} failed: ${(await new Response(tar.stderr).text()).trim()}`);
1178
+ } finally {
1179
+ await rm(`${base}.list`, { force: true });
1180
+ }
1181
+ return `${base}.tgz`;
1182
+ }
1183
+ async pushUpdate(cloudApi, devProjectId, pack, { channel, notes }) {
1184
+ const archive = await CloudRunner.#tarPack(pack);
1185
+ const target = channel ?? pack.channel;
1186
+ try {
1187
+ const release = await new UpdatePackUpload(cloudApi, devProjectId).send(archive, { appName: pack.app, target: pack.target, platform: pack.platform, channel: target, notes }).catch((error) => {
1188
+ throw CloudRunner.#refusal(error, "a pack is uploaded by a project member signed in with `akan login`");
1189
+ });
1190
+ const label = `${pack.app} ${pack.target} ${pack.platform} on channel ${target}: release ${release.id}`;
1191
+ if (release.status === "rejected") {
1192
+ process.exitCode = 2;
1193
+ Logger.error(`The project rejected ${label}:
1194
+ ${(release.problems ?? []).map((problem) => ` - ${problem}`).join(`
1195
+ `)}`);
1196
+ } else if (release.status === "published")
1197
+ Logger.rawLog(`Published ${label} (${pack.files} files, native API ${pack.nativeApi}).`);
1198
+ else
1199
+ Logger.rawLog(`Staged ${label} (${pack.files} files, native API ${pack.nativeApi}). Publish it from the project's release screen.`);
1200
+ return release;
1201
+ } finally {
1202
+ await rm(archive, { force: true });
1203
+ }
1204
+ }
1205
+ async registerBinary(cloudApi, devProjectId, app, pack, { distribution, env }) {
1206
+ const [mobileTarget] = await resolveMobileTargets(app, pack.target);
1207
+ if (!mobileTarget)
1208
+ throw new Error(`Native target '${pack.target}' was not found.`);
1209
+ const bundlePath = path7.join(new NativeApp(app, mobileTarget).outDir(pack.platform), "bundle.json");
1210
+ const bundleFile = Bun.file(bundlePath);
1211
+ if (!await bundleFile.exists())
1212
+ throw new Error(`No store build at ${bundlePath}: run \`akan release-${pack.platform} ${app.name} --target ${pack.target} --env ${env}\` first.`);
1213
+ const bundle = await bundleFile.json();
1214
+ if (!bundle.updates)
1215
+ throw new Error(`The store build at ${bundlePath} takes no updates: give the target native.updates.`);
1216
+ if (bundle.nativeApi?.hash !== pack.nativeApi)
1217
+ throw new Error(`The store build at ${bundlePath} provides native API ${bundle.nativeApi?.hash}, and this commit's pack needs ${pack.nativeApi}: build the store binary from this commit.`);
1218
+ const archive = await CloudRunner.#tarPack(pack);
1219
+ const channel = bundle.updates.channel ?? pack.channel;
1220
+ const upload = new UpdatePackUpload(cloudApi, devProjectId);
1221
+ try {
1222
+ const binary = await upload.stage(archive, { appName: pack.app, target: pack.target, platform: pack.platform, channel, notes: null }).then((packUploadId) => upload.settle(() => cloudApi.registerAppBinary(devProjectId, {
1223
+ appName: pack.app,
1224
+ target: pack.target,
1225
+ distribution,
1226
+ bundle: bundleFile,
1227
+ packUploadId,
1228
+ channel
1229
+ }))).catch((error) => {
1230
+ throw CloudRunner.#refusal(error, "a binary is registered by a project member signed in with `akan login`");
1231
+ });
1232
+ Logger.rawLog(`Registered ${pack.app} ${pack.target} ${pack.platform} (${distribution}): binary ${binary.id}, native API ${pack.nativeApi}.`);
1233
+ return binary;
1234
+ } finally {
1235
+ await rm(archive, { force: true });
1236
+ }
1237
+ }
1238
+ static formatSecrets(secrets) {
1239
+ if (!secrets.length)
1240
+ return "No pipeline secrets.";
1241
+ const rows = [...secrets].sort((a, b) => a.name.localeCompare(b.name) || a.scope.localeCompare(b.scope)).map((secret) => [
1242
+ secret.name,
1243
+ secret.scope === "*" ? "every branch" : secret.scope,
1244
+ secret.updatedAt,
1245
+ secret.updatedBy
1246
+ ]);
1247
+ const header = ["NAME", "SCOPE", "UPDATED", "BY"];
1248
+ const widths = header.map((title, column) => Math.max(title.length, ...rows.map((row) => row[column]?.length ?? 0)));
1249
+ return [header, ...rows].map((row) => row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd()).join(`
1250
+ `);
1251
+ }
1252
+ static #secretBytes = 64 * 1024;
1253
+ static #secretBranches = ["debug", "develop", "main"];
1254
+ static #secretsUnavailable = "The server is not configured for pipeline secrets yet.";
1255
+ static #assertSecretName(name) {
1256
+ const problem = PipelineNames.secretNameError(name);
1257
+ if (problem)
1258
+ throw new Error(problem);
1259
+ }
1260
+ static #assertSecretBranch(branch) {
1261
+ if (branch !== null && !CloudRunner.#secretBranches.includes(branch))
1262
+ throw new Error(`--branch ${branch} is not a deployed branch: ${CloudRunner.#secretBranches.join(", ")}.`);
1263
+ }
1264
+ static #refusal(error, who, { unavailable } = {}) {
1265
+ const message = error instanceof Error ? error.message : String(error);
1266
+ const status = CloudApi.statusOf(error);
1267
+ if (status === 401)
1268
+ return new Error("The server did not accept your sign-in: run `akan login`, then try again.");
1269
+ if (status === 403)
1270
+ return new Error(`The project refused: ${who}.`);
1271
+ if (status === 400) {
1272
+ const key = /"error"\s*:\s*"([^"]+)"/.exec(message)?.[1];
1273
+ return new Error(`The project refused the request${key ? `: ${key}` : ""}.`);
1274
+ }
1275
+ if (status === 503 && unavailable)
1276
+ return new Error(unavailable);
1277
+ return error;
1278
+ }
1083
1279
  async#syncSecretGitignore(workspace, appNames) {
1084
1280
  const patterns = (await Promise.all(appNames.map(async (appName) => {
1085
1281
  const config = await AppExecutor.from(workspace, appName).getConfig();
@@ -1,16 +1,16 @@
1
1
  // @bun
2
2
  import {
3
3
  jsonText
4
- } from "./index-h9ht6s60.js";
4
+ } from "./index-05g3jayg.js";
5
5
  import {
6
6
  akanMcpInstallTargets
7
- } from "./index-zyw622y1.js";
7
+ } from "./index-2mx692qq.js";
8
8
  import {
9
9
  script
10
- } from "./index-q6ft9fkc.js";
10
+ } from "./index-6jjtnhkw.js";
11
11
  import {
12
12
  ContextRunner2
13
- } from "./index-j38qkvkk.js";
13
+ } from "./index-ae5pwhgs.js";
14
14
 
15
15
  // pkgs/@akanjs/cli/context/context.script.ts
16
16
  import { Logger } from "akanjs/common";
@@ -1,13 +1,13 @@
1
1
  // @bun
2
2
  import {
3
3
  AppExecutor
4
- } from "./index-50wb2a26.js";
4
+ } from "./index-55mynnqs.js";
5
5
  import {
6
6
  jsonText,
7
7
  createWorkflowBaselineSummary,
8
8
  buildAkanModuleContextIndex,
9
9
  toolingRolloutGate
10
- } from "./index-h9ht6s60.js";
10
+ } from "./index-05g3jayg.js";
11
11
  import {
12
12
  buildResourceList,
13
13
  codexMcpConfigPath,
@@ -17,25 +17,27 @@ import {
17
17
  upsertCodexMcpServerBlock,
18
18
  renderDoctorText,
19
19
  AkanContextAnalyzer
20
- } from "./index-zyw622y1.js";
20
+ } from "./index-2mx692qq.js";
21
21
  import {
22
22
  runner
23
- } from "./index-q6ft9fkc.js";
23
+ } from "./index-6jjtnhkw.js";
24
24
  import {
25
- getMobileTargets,
26
- appIdsOf,
27
25
  NativeApp,
28
26
  isPlaceholderAppId
29
- } from "./index-jk9xnck6.js";
27
+ } from "./index-1w2qaee2.js";
30
28
  import {
31
29
  Prompter2
32
30
  } from "./index-zzvvc845.js";
33
31
  import {
34
32
  RepairRunner
35
- } from "./index-hp3stk1m.js";
33
+ } from "./index-fm46xrf1.js";
36
34
  import {
37
35
  WorkflowRunner
38
- } from "./index-bsv901fd.js";
36
+ } from "./index-rwq3ekqc.js";
37
+ import {
38
+ getMobileTargets,
39
+ appIdsOf
40
+ } from "./index-q958tqqg.js";
39
41
 
40
42
  // pkgs/@akanjs/devkit/akanMcpContract.ts
41
43
  import path from "path";
@@ -947,7 +949,7 @@ ${payload}`);
947
949
  "add-enum-field": "`akan add-enum-field --app <app-or-lib> --module <module> --field <field> --values a,b --format json` adds an enum field to source constant/dictionary files without editing generated files.",
948
950
  sync: "`akan sync <app-or-lib>` refreshes generated Akan files from source conventions.",
949
951
  lint: "`akan lint <app-or-lib-or-pkg>` runs Biome linting after preparing generated files.",
950
- typecheck: "`akan typecheck <app-name>` runs an application typecheck after preparing generated files.",
952
+ typecheck: "`akan typecheck <app-or-lib>` typechecks an app or a library after preparing generated files, its tests and specs included (`--tests false` to leave them out; a build does).",
951
953
  test: "`akan test <app-or-lib-or-pkg>` prepares the target and runs its tests.",
952
954
  build: "`akan build <app-name>` creates a production build after preparing generated files.",
953
955
  doctor: "`akan doctor --strict --format json` reports agent-readable workspace convention diagnostics and validation hints.",
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  AsyncDefaultExportDetector,
11
11
  resolveSsrPageEntriesForApp
12
- } from "./index-50wb2a26.js";
12
+ } from "./index-55mynnqs.js";
13
13
 
14
14
  // pkgs/@akanjs/devkit/artifact/routeSeedIndex.ts
15
15
  import path from "path";
@@ -2103,13 +2103,7 @@ import path11 from "path";
2103
2103
  var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
2104
2104
  var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
2105
2105
  var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
2106
- var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
2107
- "@libsql/client",
2108
- "bullmq",
2109
- "ioredis",
2110
- "postgres",
2111
- "protobufjs"
2112
- ]);
2106
+ var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set(["bullmq", "ioredis", "postgres", "protobufjs"]);
2113
2107
  var RUNTIME_EXTERNAL_EXACT = new Set([
2114
2108
  "react",
2115
2109
  "react-dom",
@@ -2319,10 +2313,12 @@ class PagesBundleBuilder {
2319
2313
  #command;
2320
2314
  #pageEntries;
2321
2315
  #started = Date.now();
2322
- constructor(app, command = "start", pageEntries) {
2316
+ #serverEnv;
2317
+ constructor(app, command = "start", pageEntries, serverEnv) {
2323
2318
  this.#app = app;
2324
2319
  this.#command = command;
2325
2320
  this.#pageEntries = pageEntries;
2321
+ this.#serverEnv = serverEnv;
2326
2322
  }
2327
2323
  async build() {
2328
2324
  const akanConfig = await this.#app.getConfig();
@@ -2349,6 +2345,7 @@ class PagesBundleBuilder {
2349
2345
  define: bundleDefine(this.#app, this.#command, "ssr"),
2350
2346
  metafile: dev,
2351
2347
  plugins: [
2348
+ ...this.#serverEnv ? [this.#serverEnv] : [],
2352
2349
  PagesBundleBuilder.createPagesEntryPlugin(entrySource),
2353
2350
  PagesBundleBuilder.createCssStubPlugin(),
2354
2351
  PagesBundleBuilder.createServerUseClientFetchPlugin(),
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  runner,
4
4
  script
5
- } from "./index-q6ft9fkc.js";
5
+ } from "./index-6jjtnhkw.js";
6
6
  import {
7
7
  LibSource,
8
8
  formatLibStatuses,
9
9
  LibExecutor
10
- } from "./index-50wb2a26.js";
10
+ } from "./index-55mynnqs.js";
11
11
 
12
12
  // pkgs/@akanjs/cli/library/library.script.ts
13
13
  import { Logger } from "akanjs/common";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  resolveSsrPageEntriesForApp
4
- } from "./index-50wb2a26.js";
4
+ } from "./index-55mynnqs.js";
5
5
  import {
6
6
  computeRouteSeedIndex,
7
7
  saveRouteSeedIndex,
@@ -16,7 +16,7 @@ import {
16
16
  CsrEntryFiles,
17
17
  PagesEntrySourceGenerator,
18
18
  PagesBundleBuilder
19
- } from "./index-gtjgnv36.js";
19
+ } from "./index-aprt7pkz.js";
20
20
  import {
21
21
  SOURCE_EXTS
22
22
  } from "./index-k9rrten6.js";
@@ -135,10 +135,12 @@ class CsrArtifactBuilder {
135
135
  #app;
136
136
  #command;
137
137
  #lang;
138
- constructor(app, command = "start", lang = "en") {
138
+ #cssAssets;
139
+ constructor(app, command = "start", lang = "en", { cssAssets = null } = {}) {
139
140
  this.#app = app;
140
141
  this.#command = command;
141
142
  this.#lang = lang;
143
+ this.#cssAssets = cssAssets;
142
144
  }
143
145
  async build() {
144
146
  const pageKeys = await this.#app.getPageKeys();
@@ -238,6 +240,8 @@ void bootCsr(pages);
238
240
  ];
239
241
  }
240
242
  async#loadCssAssets() {
243
+ if (this.#cssAssets)
244
+ return this.#cssAssets;
241
245
  const artifactFile = Bun.file(path2.join(this.#artifactDir, "base-artifact.json"));
242
246
  if (!await artifactFile.exists())
243
247
  return {};
@@ -355,10 +359,10 @@ import { compile } from "tailwindcss";
355
359
 
356
360
  // pkgs/@akanjs/devkit/frontendBuild/cssCandidateCache.ts
357
361
  import { stat } from "fs/promises";
358
- var CANDIDATE_RE = /-?(?:[\w@]|\[[^\]\s]+\])[\w:/.-]*(?:\[[^\]\s]+\][\w:/.-]*)*/g;
362
+ var CANDIDATE_RE = /-?(?:[\w@]|\[[^\]\s]+\])[\w:/.-]*(?:(?:\[[^\]\s]+\]|\((?:[a-z-]+:)?--[^)\s]+\))[\w:/.-]*)*/g;
359
363
 
360
364
  class CssCandidateCache {
361
- static #version = 3;
365
+ static #version = 4;
362
366
  #path;
363
367
  #entries = new Map;
364
368
  #dirty = false;
@@ -1604,9 +1608,11 @@ class SsrBaseArtifactBuilder {
1604
1608
  #artifactDir;
1605
1609
  #absArtifactDir;
1606
1610
  #started = Date.now();
1607
- constructor(app, command = "start") {
1611
+ #serverEnv;
1612
+ constructor(app, command = "start", { serverEnv } = {}) {
1608
1613
  this.#app = app;
1609
1614
  this.#command = command;
1615
+ this.#serverEnv = serverEnv;
1610
1616
  this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
1611
1617
  this.#absArtifactDir = path8.resolve(this.#artifactDir);
1612
1618
  }
@@ -1620,7 +1626,7 @@ class SsrBaseArtifactBuilder {
1620
1626
  this.#app.verbose(`[base-artifact] discovered ${pageKeys.length} route files under ${this.#app.cwdPath}/page`);
1621
1627
  const pageEntries = await resolveSsrPageEntriesForApp(this.#app, pageKeys);
1622
1628
  const { cssCompiler, optimizedFonts, cssAssets } = await this.#buildStyleAssets();
1623
- const pagesBundle = await new PagesBundleBuilder(this.#app, this.#command, pageEntries).build();
1629
+ const pagesBundle = await new PagesBundleBuilder(this.#app, this.#command, pageEntries, this.#serverEnv).build();
1624
1630
  this.#app.verbose(`[base-artifact] pages bundle -> ${pagesBundle.bundlePath} (buildId=${pagesBundle.buildId}, splitting=${pagesBundle.splitting}, entry=${pagesBundle.entryBytes} bytes, outputs=${pagesBundle.outputCount}, chunks=${pagesBundle.chunkCount}, total=${pagesBundle.outputBytes} bytes)`);
1625
1631
  const seedIndex = computeRouteSeedIndex(pageEntries);
1626
1632
  const seedIndexPath = await saveRouteSeedIndex(this.#absArtifactDir, seedIndex, {
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  runner,
4
4
  script
5
- } from "./index-q6ft9fkc.js";
5
+ } from "./index-6jjtnhkw.js";
6
6
  import {
7
7
  generatedFilesForSync,
8
8
  createPassedPrimitiveReport,
9
9
  scalarChangedFiles
10
- } from "./index-h9ht6s60.js";
10
+ } from "./index-05g3jayg.js";
11
11
  import {
12
12
  pluralizeName
13
13
  } from "./index-nz8prx9g.js";
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  import {
3
3
  script
4
- } from "./index-q6ft9fkc.js";
4
+ } from "./index-6jjtnhkw.js";
5
5
  import {
6
6
  AppExecutor,
7
7
  LibExecutor,
8
8
  ModuleExecutor
9
- } from "./index-50wb2a26.js";
9
+ } from "./index-55mynnqs.js";
10
10
  import {
11
11
  workflowStatus,
12
12
  compactDiagnostics,
@@ -41,10 +41,10 @@ import {
41
41
  hasSignalFactoryEntry,
42
42
  insertSignalFactoryEntry,
43
43
  addFieldUiPolicyForType
44
- } from "./index-h9ht6s60.js";
44
+ } from "./index-05g3jayg.js";
45
45
  import {
46
46
  ModuleScript
47
- } from "./index-f4e08ptp.js";
47
+ } from "./index-e0cvze1s.js";
48
48
 
49
49
  // pkgs/@akanjs/cli/primitive/primitive.script.ts
50
50
  import { capitalize, lowerlize } from "akanjs/common";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  runner,
4
4
  script
5
- } from "./index-q6ft9fkc.js";
5
+ } from "./index-6jjtnhkw.js";
6
6
  import {
7
7
  ModuleExecutor
8
- } from "./index-50wb2a26.js";
8
+ } from "./index-55mynnqs.js";
9
9
  import {
10
10
  sourceFile,
11
11
  moduleSourcePaths,
@@ -13,10 +13,10 @@ import {
13
13
  createPassedPrimitiveReport,
14
14
  bilingualLabelForField,
15
15
  bilingualDescriptionForField
16
- } from "./index-h9ht6s60.js";
16
+ } from "./index-05g3jayg.js";
17
17
  import {
18
18
  PageScript
19
- } from "./index-g38nhv87.js";
19
+ } from "./index-2etcv0sc.js";
20
20
  import {
21
21
  pluralizeName
22
22
  } from "./index-nz8prx9g.js";