pakstr 0.0.1 → 0.0.4

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 (59) hide show
  1. package/README.md +33 -0
  2. package/android-template/app/build.gradle.kts +2 -2
  3. package/android-template/app/src/androidTest/java/com/{nostrappshell → pakstr}/app/ExampleInstrumentedTest.kt +1 -1
  4. package/android-template/app/src/main/AndroidManifest.xml +2 -1
  5. package/android-template/app/src/main/assets/www/.gitkeep +0 -0
  6. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ApplicationClass.kt +2 -2
  7. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/Config.kt +1 -1
  8. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/LocalServer.kt +3 -3
  9. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/MainActivity.kt +68 -20
  10. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ShellRuntime.kt +7 -4
  11. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/WebViewController.kt +17 -8
  12. package/android-template/app/src/main/java/com/pakstr/app/permissions/PermissionManager.kt +78 -0
  13. package/android-template/app/src/main/java/com/pakstr/app/permissions/interfaces/PermissionHandler.kt +7 -0
  14. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/AmberSigner.kt +2 -2
  15. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/BunkerSigner.kt +2 -2
  16. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/FakeSigner.kt +2 -2
  17. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/NostrBridge.kt +1 -2
  18. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/SignerManager.kt +2 -9
  19. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/SignerType.kt +1 -1
  20. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/interfaces/NostrSigner.kt +1 -1
  21. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Color.kt +1 -1
  22. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Theme.kt +1 -1
  23. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Type.kt +1 -1
  24. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/utils/AppPreferences.kt +1 -1
  25. package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/utils/SignerPickerBottomSheet.kt +3 -3
  26. package/android-template/app/src/test/java/com/{nostrappshell → pakstr}/app/ExampleUnitTest.kt +1 -1
  27. package/bin/cli.js +5 -2
  28. package/dist/android/branding.js +23 -0
  29. package/dist/android/gradle.js +17 -0
  30. package/dist/android/icon.js +105 -0
  31. package/dist/android/permissions.js +33 -0
  32. package/dist/android/splash.js +62 -0
  33. package/dist/android/template.js +142 -0
  34. package/dist/android/version.js +16 -0
  35. package/dist/cli.js +56 -0
  36. package/dist/commands/build.js +159 -0
  37. package/dist/config.js +4 -0
  38. package/dist/core/buildContext.js +100 -0
  39. package/dist/core/config.js +24 -0
  40. package/dist/core/copyFile.js +17 -0
  41. package/dist/core/manifest.js +28 -0
  42. package/dist/core/resolveProjectInputs.js +20 -0
  43. package/dist/core/validateProject.js +21 -0
  44. package/dist/core/zeroConfig.js +97 -0
  45. package/dist/index.js +5 -0
  46. package/dist/runners/BuildRunner.js +2 -0
  47. package/dist/runners/DockerGradleRunner.js +60 -0
  48. package/dist/runners/LocalGradleRunner.js +40 -0
  49. package/dist/runners/createRunner.js +15 -0
  50. package/package.json +28 -20
  51. package/android-template/app/src/main/assets/www/assets/hero-CLDdwZDr.png +0 -0
  52. package/android-template/app/src/main/assets/www/assets/index-CsUDhMuy.css +0 -1
  53. package/android-template/app/src/main/assets/www/assets/index-D-e3tQW5.js +0 -56
  54. package/android-template/app/src/main/assets/www/assets/vite-BF8QNONU.svg +0 -1
  55. package/android-template/app/src/main/assets/www/favicon.svg +0 -1
  56. package/android-template/app/src/main/assets/www/icons.svg +0 -24
  57. package/android-template/app/src/main/assets/www/index.html +0 -14
  58. package/android-template/app/src/main/assets/www/test.html +0 -17
  59. package/bin/cli.ts +0 -5
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.patchIcon = patchIcon;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const sharp_1 = __importDefault(require("sharp"));
10
+ async function patchIcon(androidRoot, iconPath, backgroundColor = "#FFFFFF") {
11
+ if (!iconPath) {
12
+ console.log("ℹ️ No icon provided");
13
+ return;
14
+ }
15
+ const absoluteIconPath = path_1.default.resolve(process.cwd(), iconPath);
16
+ if (!fs_1.default.existsSync(absoluteIconPath)) {
17
+ throw new Error(`❌ Icon not found: ${absoluteIconPath}`);
18
+ }
19
+ const resPath = path_1.default.join(androidRoot, "app/src/main/res");
20
+ //
21
+ // 1. LEGACY ICONS
22
+ //
23
+ const sizes = {
24
+ "mipmap-mdpi": 48,
25
+ "mipmap-hdpi": 72,
26
+ "mipmap-xhdpi": 96,
27
+ "mipmap-xxhdpi": 144,
28
+ "mipmap-xxxhdpi": 192,
29
+ };
30
+ for (const folder of Object.keys(sizes)) {
31
+ const dir = path_1.default.join(resPath, folder);
32
+ fs_1.default.mkdirSync(dir, {
33
+ recursive: true,
34
+ });
35
+ await (0, sharp_1.default)(absoluteIconPath)
36
+ .resize(sizes[folder], sizes[folder], {
37
+ fit: "contain",
38
+ })
39
+ .webp()
40
+ .toFile(path_1.default.join(dir, "ic_launcher.webp"));
41
+ await (0, sharp_1.default)(absoluteIconPath)
42
+ .resize(sizes[folder], sizes[folder], {
43
+ fit: "contain",
44
+ })
45
+ .webp()
46
+ .toFile(path_1.default.join(dir, "ic_launcher_round.webp"));
47
+ console.log(`🖼️ Generated ${folder}`);
48
+ }
49
+ //
50
+ // 2. ADAPTIVE FOREGROUND
51
+ //
52
+ const drawablePath = path_1.default.join(resPath, "drawable");
53
+ fs_1.default.mkdirSync(drawablePath, {
54
+ recursive: true,
55
+ });
56
+ await (0, sharp_1.default)(absoluteIconPath)
57
+ .resize(432, 432, {
58
+ fit: "contain",
59
+ background: {
60
+ r: 0,
61
+ g: 0,
62
+ b: 0,
63
+ alpha: 0,
64
+ },
65
+ })
66
+ .webp()
67
+ .toFile(path_1.default.join(drawablePath, "ic_launcher_foreground.webp"));
68
+ console.log("🖼️ Generated adaptive foreground");
69
+ //
70
+ // 3. REMOVE OLD VECTOR FOREGROUND
71
+ //
72
+ const oldXml = path_1.default.join(drawablePath, "ic_launcher_foreground.xml");
73
+ if (fs_1.default.existsSync(oldXml)) {
74
+ fs_1.default.unlinkSync(oldXml);
75
+ }
76
+ //
77
+ // 4. BACKGROUND COLOR
78
+ //
79
+ const valuesPath = path_1.default.join(resPath, "values");
80
+ fs_1.default.mkdirSync(valuesPath, {
81
+ recursive: true,
82
+ });
83
+ fs_1.default.writeFileSync(path_1.default.join(valuesPath, "launcher_colors.xml"), `<?xml version="1.0" encoding="utf-8"?>
84
+ <resources>
85
+ <color name="ic_launcher_background">${backgroundColor}</color>
86
+ </resources>`);
87
+ //
88
+ // 5. ADAPTIVE XML
89
+ //
90
+ const adaptiveDir = path_1.default.join(resPath, "mipmap-anydpi-v26");
91
+ fs_1.default.mkdirSync(adaptiveDir, {
92
+ recursive: true,
93
+ });
94
+ const adaptiveXml = `<?xml version="1.0" encoding="utf-8"?>
95
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
96
+
97
+ <background android:drawable="@color/ic_launcher_background"/>
98
+
99
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
100
+
101
+ </adaptive-icon>`;
102
+ fs_1.default.writeFileSync(path_1.default.join(adaptiveDir, "ic_launcher.xml"), adaptiveXml);
103
+ fs_1.default.writeFileSync(path_1.default.join(adaptiveDir, "ic_launcher_round.xml"), adaptiveXml);
104
+ console.log("✅ Adaptive icon patched");
105
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.patchPermissions = patchPermissions;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const permissionMap = {
10
+ internet: "android.permission.INTERNET",
11
+ camera: "android.permission.CAMERA",
12
+ notifications: "android.permission.POST_NOTIFICATIONS",
13
+ };
14
+ function patchPermissions(androidRoot, manifest) {
15
+ if (!manifest.permissions?.length) {
16
+ return;
17
+ }
18
+ const file = path_1.default.join(androidRoot, "app/src/main/AndroidManifest.xml");
19
+ let xml = fs_1.default.readFileSync(file, "utf-8");
20
+ for (const perm of manifest.permissions) {
21
+ const androidPermission = permissionMap[perm];
22
+ if (!androidPermission) {
23
+ console.warn(`⚠️ Unknown permission: ${perm}`);
24
+ continue;
25
+ }
26
+ const line = `<uses-permission android:name="${androidPermission}"/>`;
27
+ if (!xml.includes(line)) {
28
+ xml = xml.replace("<manifest", `<manifest\n ${line}`);
29
+ }
30
+ }
31
+ fs_1.default.writeFileSync(file, xml);
32
+ console.log("🔐 Permissions patched");
33
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.patchSplash = patchSplash;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const sharp_1 = __importDefault(require("sharp"));
10
+ async function patchSplash(androidRoot, splashPath, backgroundColor) {
11
+ if (!splashPath) {
12
+ console.log("ℹ️ No splash provided");
13
+ return;
14
+ }
15
+ const absolutePath = path_1.default.resolve(splashPath);
16
+ if (!fs_1.default.existsSync(absolutePath)) {
17
+ throw new Error(`❌ Splash not found: ${absolutePath}`);
18
+ }
19
+ // -----------------------------
20
+ // GENERATE SPLASH IMAGE
21
+ // -----------------------------
22
+ const splashOutput = path_1.default.join(androidRoot, "app/src/main/res/drawable/splash_image.webp");
23
+ await (0, sharp_1.default)(absolutePath)
24
+ .resize(512, 512, {
25
+ fit: "contain",
26
+ })
27
+ .webp()
28
+ .toFile(splashOutput);
29
+ console.log("🖼️ Generated splash image");
30
+ // -----------------------------
31
+ // PATCH COLORS.XML
32
+ // -----------------------------
33
+ if (backgroundColor) {
34
+ const colorsFile = path_1.default.join(androidRoot, "app/src/main/res/values/colors.xml");
35
+ if (fs_1.default.existsSync(colorsFile)) {
36
+ let colors = fs_1.default.readFileSync(colorsFile, "utf-8");
37
+ if (colors.includes("splash_background")) {
38
+ colors = colors.replace(/<color name="splash_background">.*<\/color>/, `<color name="splash_background">${backgroundColor}</color>`);
39
+ }
40
+ else {
41
+ colors = colors.replace("</resources>", `
42
+ <color name="splash_background">${backgroundColor}</color>
43
+ </resources>`);
44
+ }
45
+ fs_1.default.writeFileSync(colorsFile, colors);
46
+ console.log("🎨 Splash background patched");
47
+ }
48
+ }
49
+ // -----------------------------
50
+ // PATCH THEME
51
+ // -----------------------------
52
+ const themeFile = path_1.default.join(androidRoot, "app/src/main/res/values/themes.xml");
53
+ if (!fs_1.default.existsSync(themeFile)) {
54
+ console.log("⚠️ Theme file not found, skipping theme patch");
55
+ return;
56
+ }
57
+ let theme = fs_1.default.readFileSync(themeFile, "utf-8");
58
+ theme = theme.replace("@color/white", "@color/splash_background");
59
+ theme = theme.replace("@mipmap/ic_launcher", "@drawable/splash_image");
60
+ fs_1.default.writeFileSync(themeFile, theme);
61
+ console.log("✅ Splash patched");
62
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ensureTemplate = ensureTemplate;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const node_fetch_1 = __importDefault(require("node-fetch"));
11
+ const unzipper_1 = __importDefault(require("unzipper"));
12
+ const TEMPLATE_URL = "https://git.nostrdev.com/stuff/pakstr/archive/main.zip";
13
+ function getCacheDir() {
14
+ return path_1.default.join(os_1.default.homedir(), ".nostrappshell");
15
+ }
16
+ function getTemplatePath() {
17
+ return path_1.default.join(getCacheDir(), "android-template");
18
+ }
19
+ // -----------------------------
20
+ // FIND TEMPLATE ROOT (RECURSIVE)
21
+ // -----------------------------
22
+ function findTemplateRoot(dir) {
23
+ const entries = fs_1.default.readdirSync(dir);
24
+ for (const e of entries) {
25
+ const full = path_1.default.join(dir, e);
26
+ if (!fs_1.default.statSync(full).isDirectory())
27
+ continue;
28
+ if (fs_1.default.existsSync(path_1.default.join(full, "app/build.gradle.kts"))) {
29
+ return full;
30
+ }
31
+ const nested = findTemplateRoot(full);
32
+ if (nested)
33
+ return nested;
34
+ }
35
+ return null;
36
+ }
37
+ // -----------------------------
38
+ // FIX GRADLEW PERMISSIONS
39
+ // -----------------------------
40
+ function fixGradlewPermissions(root) {
41
+ const possiblePaths = [
42
+ path_1.default.join(root, "gradlew"),
43
+ path_1.default.join(root, "app", "gradlew"),
44
+ ];
45
+ for (const p of possiblePaths) {
46
+ if (fs_1.default.existsSync(p)) {
47
+ fs_1.default.chmodSync(p, 0o755);
48
+ console.log("🔐 gradlew permission fixed:", p);
49
+ }
50
+ }
51
+ }
52
+ // -----------------------------
53
+ // CREATE LOCAL.PROPERTIES
54
+ // -----------------------------
55
+ function createLocalProperties(root) {
56
+ const sdkPath = process.env.ANDROID_HOME ??
57
+ path_1.default.join(os_1.default.homedir(), "Library", "Android", "sdk");
58
+ const content = `sdk.dir=${sdkPath}\n`;
59
+ const file = path_1.default.join(root, "local.properties");
60
+ fs_1.default.writeFileSync(file, content);
61
+ console.log("📍 local.properties created:", sdkPath);
62
+ }
63
+ async function ensureTemplate() {
64
+ const cacheDir = getCacheDir();
65
+ const templatePath = getTemplatePath();
66
+ const zipPath = path_1.default.join(cacheDir, "template.zip");
67
+ // -----------------------------
68
+ // BUNDLED TEMPLATE CHECK
69
+ // -----------------------------
70
+ const bundledTemplate = path_1.default.resolve(__dirname, "../../android-template");
71
+ if (fs_1.default.existsSync(path_1.default.join(bundledTemplate, "settings.gradle.kts"))) {
72
+ console.log("📦 Using bundled Android template");
73
+ if (fs_1.default.existsSync(templatePath)) {
74
+ fs_1.default.rmSync(templatePath, {
75
+ recursive: true,
76
+ force: true
77
+ });
78
+ }
79
+ fs_1.default.cpSync(bundledTemplate, templatePath, {
80
+ recursive: true
81
+ });
82
+ fixGradlewPermissions(templatePath);
83
+ createLocalProperties(templatePath);
84
+ return templatePath;
85
+ }
86
+ // -----------------------------
87
+ // CACHE CHECK
88
+ // -----------------------------
89
+ if (fs_1.default.existsSync(path_1.default.join(templatePath, "settings.gradle.kts"))) {
90
+ console.log("📦 Using cached Android template");
91
+ return templatePath;
92
+ }
93
+ console.log("📥 Downloading Android template...");
94
+ fs_1.default.mkdirSync(cacheDir, { recursive: true });
95
+ // -----------------------------
96
+ // DOWNLOAD ZIP
97
+ // -----------------------------
98
+ const res = await (0, node_fetch_1.default)(TEMPLATE_URL);
99
+ if (!res.ok || !res.body) {
100
+ throw new Error("Failed to download template");
101
+ }
102
+ const fileStream = fs_1.default.createWriteStream(zipPath);
103
+ const body = res.body;
104
+ await new Promise((resolve, reject) => {
105
+ body.pipe(fileStream);
106
+ body.on("error", reject);
107
+ fileStream.on("error", reject);
108
+ fileStream.on("finish", resolve);
109
+ });
110
+ console.log("📦 Extracting template...");
111
+ // -----------------------------
112
+ // EXTRACT ZIP
113
+ // -----------------------------
114
+ await new Promise((resolve, reject) => {
115
+ fs_1.default.createReadStream(zipPath)
116
+ .pipe(unzipper_1.default.Extract({ path: cacheDir }))
117
+ .on("close", resolve)
118
+ .on("error", reject);
119
+ });
120
+ // -----------------------------
121
+ // FIND ANDROID ROOT
122
+ // -----------------------------
123
+ const extractedPath = findTemplateRoot(cacheDir);
124
+ if (!extractedPath) {
125
+ throw new Error("Template extract failed - no valid Android project found");
126
+ }
127
+ // -----------------------------
128
+ // MOVE TO FINAL LOCATION
129
+ // -----------------------------
130
+ if (fs_1.default.existsSync(templatePath)) {
131
+ fs_1.default.rmSync(templatePath, { recursive: true, force: true });
132
+ }
133
+ fs_1.default.renameSync(extractedPath, templatePath);
134
+ fs_1.default.unlinkSync(zipPath);
135
+ console.log("✅ Template ready at:", templatePath);
136
+ // -----------------------------
137
+ // FIX PERMISSIONS (IMPORTANT)
138
+ // -----------------------------
139
+ fixGradlewPermissions(templatePath);
140
+ createLocalProperties(templatePath);
141
+ return templatePath;
142
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.patchVersion = patchVersion;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function patchVersion(androidRoot, versionCode, versionName) {
10
+ const gradlePath = path_1.default.join(androidRoot, "app", "build.gradle.kts");
11
+ let gradle = fs_1.default.readFileSync(gradlePath, "utf8");
12
+ gradle = gradle.replace(/versionCode\s*=\s*[^\r\n]+/, `versionCode = ${versionCode}`);
13
+ gradle = gradle.replace(/versionName\s*=\s*"[^"]+"/, `versionName = "${versionName}"`);
14
+ fs_1.default.writeFileSync(gradlePath, gradle);
15
+ console.log("✅ Version patched:", versionCode, versionName);
16
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runCLI = runCLI;
7
+ const build_1 = require("./commands/build");
8
+ const package_json_1 = __importDefault(require("../package.json"));
9
+ async function runCLI(argv) {
10
+ if (argv.includes("--help") || argv.includes("-h")) {
11
+ printHelp();
12
+ return;
13
+ }
14
+ if (argv.includes("--version") || argv.includes("-v")) {
15
+ console.log(package_json_1.default.version);
16
+ return;
17
+ }
18
+ const command = argv[0];
19
+ const subcommand = argv[1];
20
+ const args = argv.slice(2);
21
+ if (!command) {
22
+ printHelp();
23
+ return;
24
+ }
25
+ switch (command) {
26
+ case "build":
27
+ if (subcommand === "apk") {
28
+ console.log("🚀 Running: build apk");
29
+ return await (0, build_1.buildCommand)(args);
30
+ }
31
+ console.error("❌ Missing subcommand. Use: build apk");
32
+ process.exitCode = 1;
33
+ return;
34
+ default:
35
+ console.error(`❌ Unknown command: ${command}`);
36
+ printHelp();
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ }
41
+ function printHelp() {
42
+ console.log(`
43
+ 🚀 Pakstr CLI
44
+
45
+ Usage:
46
+
47
+ pakstr build apk --manifest <path> --web <path> --out <path>
48
+
49
+ Example:
50
+
51
+ pakstr build apk \
52
+ --manifest app.manifest.json \
53
+ --web ./dist \
54
+ --out ./build/app.apk
55
+ `);
56
+ }
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildCommand = buildCommand;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const child_process_1 = require("child_process");
10
+ const gradle_1 = require("../android/gradle");
11
+ const validateProject_1 = require("../core/validateProject");
12
+ const buildContext_1 = require("../core/buildContext");
13
+ const template_1 = require("../android/template");
14
+ const createRunner_1 = require("../runners/createRunner");
15
+ const config_1 = require("../core/config");
16
+ const branding_1 = require("../android/branding");
17
+ const version_1 = require("../android/version");
18
+ const icon_1 = require("../android/icon");
19
+ const splash_1 = require("../android/splash");
20
+ const permissions_1 = require("../android/permissions");
21
+ async function buildCommand(args) {
22
+ const ctx = (0, buildContext_1.createBuildContext)(args);
23
+ const config = (0, config_1.loadConfig)(process.cwd());
24
+ const manifest = ctx.manifest;
25
+ console.log("📄 Manifest loaded");
26
+ const distPath = ctx.dist;
27
+ const androidRoot = await (0, template_1.ensureTemplate)();
28
+ const mode = ctx.mode;
29
+ const apkOutput = ctx.out;
30
+ const assetsTarget = path_1.default.join(androidRoot, "app/src/main/assets/www");
31
+ const appName = manifest?.app?.name || manifest?.appName || "Pakstr App";
32
+ console.log("\n🚀 Pakstr CLI");
33
+ console.log("📦 App:", appName);
34
+ console.log("🌐 Web:", distPath);
35
+ console.log("📱 Out:", apkOutput);
36
+ console.log("📱 Android:", androidRoot);
37
+ console.log("🏗️ Builder:", config.builder);
38
+ console.log("🚀 BUILD MODE:", mode.toUpperCase());
39
+ // -----------------------------
40
+ // 1. VALIDATE PROJECT
41
+ // -----------------------------
42
+ (0, validateProject_1.validateProject)(distPath, manifest);
43
+ if (!fs_1.default.existsSync(distPath)) {
44
+ throw new Error("❌ Web assets not found");
45
+ }
46
+ // -----------------------------
47
+ // 2. COPY WEB → ANDROID ASSETS
48
+ // -----------------------------
49
+ console.log("\n📦 Copying web → Android assets...");
50
+ fs_1.default.rmSync(assetsTarget, {
51
+ recursive: true,
52
+ force: true,
53
+ });
54
+ fs_1.default.mkdirSync(assetsTarget, {
55
+ recursive: true,
56
+ });
57
+ copyFolder(distPath, assetsTarget);
58
+ console.log("📦 Web assets copied successfully");
59
+ // -----------------------------
60
+ // 3. PATCH ANDROID PROJECT
61
+ // -----------------------------
62
+ (0, gradle_1.patchGradle)(androidRoot, manifest);
63
+ console.log("⚙️ Gradle patched");
64
+ // -----------------------------
65
+ // 3.1 PATCH BRANDING
66
+ // -----------------------------
67
+ (0, branding_1.patchAppName)(androidRoot, appName);
68
+ (0, branding_1.patchPackageName)(androidRoot, manifest.appId);
69
+ (0, version_1.patchVersion)(androidRoot, manifest.versionCode, manifest.versionName);
70
+ const iconPath = manifest.ui?.icon;
71
+ if (iconPath) {
72
+ await (0, icon_1.patchIcon)(androidRoot, iconPath, manifest.backgroundColor ?? "#FFFFFF");
73
+ }
74
+ const splash = manifest.ui?.splash;
75
+ if (splash) {
76
+ console.log("SPLASH IMAGE:", splash.image);
77
+ console.log("SPLASH BACKGROUND:", splash.background);
78
+ await (0, splash_1.patchSplash)(androidRoot, splash.image, splash.background ?? "#FFFFFF");
79
+ }
80
+ (0, permissions_1.patchPermissions)(androidRoot, manifest);
81
+ // -----------------------------
82
+ // 4. RUN BUILD RUNNER
83
+ // -----------------------------
84
+ let apkPath;
85
+ try {
86
+ console.log("\n⚙️ Running build runner...");
87
+ const runner = (0, createRunner_1.createRunner)(config);
88
+ apkPath = await runner.build(androidRoot, mode);
89
+ console.log("✅ APK build successful");
90
+ console.log("\n📦 APK GENERATED:");
91
+ console.log(apkPath);
92
+ }
93
+ catch (err) {
94
+ console.error("❌ Build failed");
95
+ console.error(err);
96
+ throw err;
97
+ }
98
+ // -----------------------------
99
+ // 5. COPY APK TO OUTPUT
100
+ // -----------------------------
101
+ const finalPath = path_1.default.resolve(apkOutput);
102
+ fs_1.default.mkdirSync(path_1.default.dirname(finalPath), {
103
+ recursive: true,
104
+ });
105
+ if (fs_1.default.existsSync(finalPath)) {
106
+ const stat = fs_1.default.lstatSync(finalPath);
107
+ if (stat.isDirectory()) {
108
+ fs_1.default.rmSync(finalPath, {
109
+ recursive: true,
110
+ force: true,
111
+ });
112
+ }
113
+ }
114
+ fs_1.default.copyFileSync(apkPath, finalPath);
115
+ console.log("\n📦 CLEAN OUTPUT:");
116
+ console.log(finalPath);
117
+ openFolder(finalPath);
118
+ // -----------------------------
119
+ // 6. FINAL OUTPUT
120
+ // -----------------------------
121
+ console.log("\n🚀 BUILD COMPLETE");
122
+ console.log("App:", appName);
123
+ console.log("Mode:", mode.toUpperCase());
124
+ }
125
+ // -----------------------------
126
+ // COPY FOLDER
127
+ // -----------------------------
128
+ function copyFolder(src, dest) {
129
+ const entries = fs_1.default.readdirSync(src, {
130
+ withFileTypes: true,
131
+ });
132
+ for (const entry of entries) {
133
+ const srcPath = path_1.default.join(src, entry.name);
134
+ const destPath = path_1.default.join(dest, entry.name);
135
+ if (entry.isDirectory()) {
136
+ fs_1.default.mkdirSync(destPath, {
137
+ recursive: true,
138
+ });
139
+ copyFolder(srcPath, destPath);
140
+ }
141
+ else {
142
+ fs_1.default.copyFileSync(srcPath, destPath);
143
+ }
144
+ }
145
+ }
146
+ // -----------------------------
147
+ // OPEN FOLDER (macOS)
148
+ // -----------------------------
149
+ function openFolder(filePath) {
150
+ if (process.platform !== "darwin") {
151
+ return;
152
+ }
153
+ try {
154
+ (0, child_process_1.execSync)(`open "${path_1.default.dirname(filePath)}"`);
155
+ }
156
+ catch {
157
+ // ignore
158
+ }
159
+ }
package/dist/config.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TEMPLATE_URL = void 0;
4
+ exports.TEMPLATE_URL = "https://git.nostrdev.com/stuff/pakstr/-/archive/main/pakstr-main.zip";
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createBuildContext = createBuildContext;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const manifest_1 = require("./manifest");
10
+ // -----------------------------
11
+ // CONFIG LOADER
12
+ // -----------------------------
13
+ function loadConfig() {
14
+ const configPath = path_1.default.resolve(process.cwd(), "nostr.config.json");
15
+ if (!fs_1.default.existsSync(configPath)) {
16
+ return null;
17
+ }
18
+ try {
19
+ return JSON.parse(fs_1.default.readFileSync(configPath, "utf-8"));
20
+ }
21
+ catch (e) {
22
+ throw new Error("❌ Invalid nostr.config.json");
23
+ }
24
+ }
25
+ function createBuildContext(args) {
26
+ const getArg = (key) => {
27
+ const i = args.indexOf(key);
28
+ return i !== -1 ? args[i + 1] : null;
29
+ };
30
+ function resolvePath(p) {
31
+ if (!p)
32
+ return null;
33
+ // absolute path
34
+ if (path_1.default.isAbsolute(p)) {
35
+ return p;
36
+ }
37
+ // try cwd
38
+ const cwdPath = path_1.default.resolve(process.cwd(), p);
39
+ if (fs_1.default.existsSync(cwdPath)) {
40
+ return cwdPath;
41
+ }
42
+ // try one level up (VERY IMPORTANT for your case)
43
+ const parentPath = path_1.default.resolve(process.cwd(), "..", p);
44
+ if (fs_1.default.existsSync(parentPath)) {
45
+ return parentPath;
46
+ }
47
+ return cwdPath;
48
+ }
49
+ const mode = args.includes("--release") ? "release" : "debug";
50
+ // -----------------------------
51
+ // 1. LOAD CONFIG (OPTIONAL)
52
+ // -----------------------------
53
+ const config = loadConfig();
54
+ // -----------------------------
55
+ // 2. RESOLVE INPUTS (PRIORITY ORDER)
56
+ // args > config > error
57
+ // -----------------------------
58
+ const webRaw = getArg("--web") ?? config?.web;
59
+ const manifestPathRaw = getArg("--manifest") ?? config?.manifest;
60
+ const outPathRaw = getArg("--out") ?? config?.out;
61
+ const web = resolvePath(webRaw);
62
+ const manifestPath = resolvePath(manifestPathRaw);
63
+ const outPath = resolvePath(outPathRaw);
64
+ // -----------------------------
65
+ // 3. VALIDATION (STRICT)
66
+ // -----------------------------
67
+ if (!web) {
68
+ throw new Error("❌ Missing web path (--web or nostr.config.json)");
69
+ }
70
+ if (!manifestPath) {
71
+ throw new Error("❌ Missing manifest path (--manifest or nostr.config.json)");
72
+ }
73
+ if (!fs_1.default.existsSync(web)) {
74
+ throw new Error(`❌ Web folder not found: ${web}`);
75
+ }
76
+ if (!fs_1.default.existsSync(manifestPath)) {
77
+ throw new Error(`❌ Manifest not found: ${manifestPath}`);
78
+ }
79
+ // -----------------------------
80
+ // 4. LOAD MANIFEST
81
+ // -----------------------------
82
+ const manifest = (0, manifest_1.loadManifest)(manifestPath);
83
+ // -----------------------------
84
+ // 5. OUTPUT PATH
85
+ // -----------------------------
86
+ const out = outPath
87
+ ? path_1.default.resolve(outPath)
88
+ : path_1.default.join(process.cwd(), "build/app.apk");
89
+ // -----------------------------
90
+ // 6. ANDROID ROOT
91
+ // -----------------------------
92
+ const androidRoot = path_1.default.resolve(process.cwd(), "android-template");
93
+ return {
94
+ manifest,
95
+ dist: path_1.default.resolve(web),
96
+ out,
97
+ androidRoot,
98
+ mode,
99
+ };
100
+ }