pakstr 0.0.1 → 0.0.3
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/README.md +33 -0
- package/android-template/app/build.gradle.kts +2 -2
- package/android-template/app/src/androidTest/java/com/{nostrappshell → pakstr}/app/ExampleInstrumentedTest.kt +1 -1
- package/android-template/app/src/main/AndroidManifest.xml +2 -1
- package/android-template/app/src/main/assets/www/.gitkeep +0 -0
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ApplicationClass.kt +2 -2
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/Config.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/LocalServer.kt +3 -3
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/MainActivity.kt +68 -20
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ShellRuntime.kt +7 -4
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/WebViewController.kt +17 -8
- package/android-template/app/src/main/java/com/pakstr/app/permissions/PermissionManager.kt +78 -0
- package/android-template/app/src/main/java/com/pakstr/app/permissions/interfaces/PermissionHandler.kt +7 -0
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/AmberSigner.kt +2 -2
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/BunkerSigner.kt +2 -2
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/FakeSigner.kt +2 -2
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/NostrBridge.kt +1 -2
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/SignerManager.kt +2 -9
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/SignerType.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/signer/interfaces/NostrSigner.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Color.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Theme.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/ui/theme/Type.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/utils/AppPreferences.kt +1 -1
- package/android-template/app/src/main/java/com/{nostrappshell → pakstr}/app/utils/SignerPickerBottomSheet.kt +3 -3
- package/android-template/app/src/test/java/com/{nostrappshell → pakstr}/app/ExampleUnitTest.kt +1 -1
- package/bin/cli.js +5 -2
- package/dist/android/branding.js +23 -0
- package/dist/android/gradle.js +17 -0
- package/dist/android/icon.js +105 -0
- package/dist/android/permissions.js +33 -0
- package/dist/android/splash.js +62 -0
- package/dist/android/template.js +142 -0
- package/dist/android/version.js +16 -0
- package/dist/cli.js +56 -0
- package/dist/commands/build.js +159 -0
- package/dist/config.js +4 -0
- package/dist/core/buildContext.js +100 -0
- package/dist/core/config.js +24 -0
- package/dist/core/copyFile.js +17 -0
- package/dist/core/manifest.js +28 -0
- package/dist/core/resolveProjectInputs.js +20 -0
- package/dist/core/validateProject.js +21 -0
- package/dist/core/zeroConfig.js +97 -0
- package/dist/index.js +5 -0
- package/dist/runners/BuildRunner.js +2 -0
- package/dist/runners/DockerGradleRunner.js +60 -0
- package/dist/runners/LocalGradleRunner.js +40 -0
- package/dist/runners/createRunner.js +15 -0
- package/package.json +28 -20
- package/android-template/app/src/main/assets/www/assets/hero-CLDdwZDr.png +0 -0
- package/android-template/app/src/main/assets/www/assets/index-CsUDhMuy.css +0 -1
- package/android-template/app/src/main/assets/www/assets/index-D-e3tQW5.js +0 -56
- package/android-template/app/src/main/assets/www/assets/vite-BF8QNONU.svg +0 -1
- package/android-template/app/src/main/assets/www/favicon.svg +0 -1
- package/android-template/app/src/main/assets/www/icons.svg +0 -24
- package/android-template/app/src/main/assets/www/index.html +0 -14
- package/android-template/app/src/main/assets/www/test.html +0 -17
- 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,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
|
+
}
|