@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.
- package/.build-stamp +1 -1
- package/{CodeAgent-szbjv4gv.js → CodeAgent-q7a4srt6.js} +2 -2
- package/{CodeTui-5nw86ab6.js → CodeTui-ak23dxc9.js} +1 -1
- package/{TunnelShare-cwh9ezsm.js → TunnelShare-21mm6dr5.js} +2 -2
- package/{agent.command-np1dffkb.js → agent.command-f3gsc1gd.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-gk69v1xs.js → application.command-3v2d53gd.js} +37 -15
- package/{applicationBuildRunner-wx09n9g6.js → applicationBuildRunner-1yaqsrdq.js} +115 -103
- package/buildBatch.proc.js +16 -5
- package/cloud.command-kk2rwkvw.js +187 -0
- package/cloud.script-r7eyfey4.js +20 -0
- package/{code.command-hmtx14b2.js → code.command-30sca09d.js} +6 -6
- package/commandManifest.json +1 -1
- package/{context.command-6aqvdx43.js → context.command-k7eqacq6.js} +13 -13
- package/context.runner-hqd1ppqe.js +21 -0
- package/{guideline.command-axvf68gc.js → guideline.command-dtczrqpf.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +10 -2
- package/guidelines/conventions/conventions.instruction.md +68 -25
- package/guidelines/cssRule/cssRule.instruction.md +19 -0
- package/guidelines/mcpRule/mcpRule.instruction.md +67 -12
- package/guidelines/queryRule/queryRule.instruction.md +11 -1
- package/guidelines/runtimeRule/runtimeRule.instruction.md +97 -16
- package/guidelines/ssrRule/ssrRule.instruction.md +10 -7
- package/guidelines/transportRule/transportRule.instruction.md +19 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +6 -6
- package/incrementalBuilder.proc.js +24 -3
- package/{index-h9ht6s60.js → index-05g3jayg.js} +1 -1
- package/{index-j38qkvkk.js → index-0bm04n5g.js} +12 -10
- package/{index-g38nhv87.js → index-2etcv0sc.js} +1 -1
- package/{index-zyw622y1.js → index-2mx692qq.js} +3 -3
- package/{index-jb0hmt3k.js → index-2t4tqd1c.js} +3 -3
- package/{index-50wb2a26.js → index-55mynnqs.js} +354 -129
- package/{index-q6ft9fkc.js → index-6jjtnhkw.js} +8 -3
- package/index-6w0rbr9d.js +68 -0
- package/{index-2wc41k5w.js → index-7bymavqk.js} +2 -2
- package/{index-gtjgnv36.js → index-aprt7pkz.js} +6 -9
- package/{index-1bjkfvm9.js → index-c1x0be5j.js} +2 -2
- package/{index-z750gk1j.js → index-c2yrsxdh.js} +13 -7
- package/{index-cb6yc5mb.js → index-c76k723e.js} +2 -2
- package/{index-6keyf4sg.js → index-ctanya41.js} +4 -4
- package/{index-f4e08ptp.js → index-e0cvze1s.js} +4 -4
- package/{index-hp3stk1m.js → index-fm46xrf1.js} +3 -3
- package/{index-3xmzr1p1.js → index-gnyf1fqy.js} +2 -2
- package/{index-zwd84m0w.js → index-jyv387qw.js} +4 -4
- package/{index-jk9xnck6.js → index-kjvrq3nc.js} +231 -133
- package/{index-xwprn9nr.js → index-nz15k0b6.js} +1 -1
- package/{index-nwd6qzvv.js → index-p9ystjrp.js} +138 -55
- package/index-q958tqqg.js +77 -0
- package/{index-50jjwg8y.js → index-qt22xstj.js} +1 -1
- package/{index-94s080qm.js → index-r2tpeb3n.js} +127 -1
- package/{index-bsv901fd.js → index-rwq3ekqc.js} +7 -7
- package/{index-t91a7339.js → index-tn4rpqp1.js} +96 -12
- package/{index-1gs9g12n.js → index-v29746vb.js} +4 -4
- package/index-ww9bhp67.js +62 -0
- package/{index-hxnnfy57.js → index-y8q31011.js} +180 -8
- package/index.js +22 -21
- package/{library.command-9m2rdwwz.js → library.command-gaga5bmq.js} +3 -3
- package/{localRegistry.command-kda81jyk.js → localRegistry.command-9e84e32q.js} +9 -9
- package/{module.command-q63dffya.js → module.command-7vpf6db4.js} +5 -5
- package/{package.command-y2jh0m99.js → package.command-twmvvf69.js} +3 -3
- package/package.json +2 -2
- package/{page.command-f3e4cwhh.js → page.command-5asq7fdf.js} +3 -3
- package/pipeline.command-d0v38572.js +1334 -0
- package/{primitive.command-r4kq75qj.js → primitive.command-dyrs2fm8.js} +6 -6
- package/projectTypecheck-cvtkrzp2.js +7 -0
- package/{quality.command-f7gzh26z.js → quality.command-6emsqpbg.js} +5 -4
- package/{repair.command-0qmyr6qa.js → repair.command-8t6aegv5.js} +5 -5
- package/{scalar.command-kddn7av4.js → scalar.command-bnggy1x7.js} +4 -4
- package/{subspace.command-ft65qvc4.js → subspace.command-f02j5tge.js} +158 -91
- package/templates/appSample/page/task/[taskId]/_index.tsx +2 -2
- package/templates/appSample/page/task/[taskId]/edit.tsx +2 -2
- package/templates/appSample/page/task/_index.tsx +2 -2
- package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -2
- package/templates/crudPages/[__model__Id]/page.tsx +3 -3
- package/templates/crudPages/page.tsx +2 -2
- package/templates/crudSinglePage/page.tsx +3 -3
- package/templates/lib/cnst.ts +2 -2
- package/templates/lib/sig.ts +3 -3
- package/{tunnel.command-b3ymdk4f.js → tunnel.command-9x8nfn75.js} +16 -13
- package/{workflow.command-z1srcdfw.js → workflow.command-jx0wag5r.js} +10 -10
- package/{workspace.command-vp6vmx3z.js → workspace.command-cmj6mqyk.js} +57 -27
- package/cloud.command-tw06613r.js +0 -95
- package/cloud.script-64gzba8r.js +0 -20
- 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-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
630
|
-
this.web = new NativeWebDir(
|
|
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
|
|
639
|
+
return path7.join(this.targetRoot, "build", platform);
|
|
634
640
|
}
|
|
635
641
|
devOutDir(platform) {
|
|
636
|
-
return
|
|
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:
|
|
647
|
-
publicDir:
|
|
648
|
-
fontsDir:
|
|
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
|
|
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
|
-
|
|
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
|
|
815
|
+
return path7.join(this.targetRoot, "updates");
|
|
819
816
|
}
|
|
820
|
-
async publishUpdate(platform, {
|
|
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
|
-
...
|
|
831
|
-
...
|
|
834
|
+
...arch ? { arch } : {},
|
|
835
|
+
...distribution.macos ? { macos: distribution.macos } : {},
|
|
836
|
+
...distribution.windows ? { windows: distribution.windows } : {}
|
|
832
837
|
});
|
|
833
838
|
}
|
|
834
|
-
async
|
|
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
|
|
884
|
-
|
|
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
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
|
966
|
-
import
|
|
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 =
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
1107
|
+
for (const name of await readdir3(dist))
|
|
1070
1108
|
if (!DesktopServerStage.uncarried.has(name))
|
|
1071
|
-
await cp2(
|
|
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 {
|
|
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,
|
|
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-
|
|
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({
|