phase1: SPM project setup — package, Makefile, app bundle, entry point
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 9 changed files with +472 and −1
added
Makefile
+89 −0
@@ -0,0 +1,89 @@ | ||
| 1 | +# | |
| 2 | +# Makefile | |
| 3 | +# Zyquo Router | |
| 4 | +# | |
| 5 | +# Author: Simon-Pierre Boucher | |
| 6 | +# Mail: contact@spboucher.ai | |
| 7 | +# | |
| 8 | +# make / make dev → release build, assemble dist/Zyquo Router.app, ad-hoc sign (fast local iteration) | |
| 9 | +# make release → universal binary, Developer ID sign, notarize, staple (Phase 8) | |
| 10 | +# make icon → regenerate AppIcon.icns from assets/icon/zyquo-router.svg (Phase 5) | |
| 11 | +# make test → swift test | |
| 12 | +# make clean → remove build products | |
| 13 | +# | |
| 14 | +# Unlike Zyquo Cloud (CLT-only machine), full Xcode is installed here, so the | |
| 15 | +# SwiftUI macro plugins ship with the toolchain and no SDKROOT pin is needed. | |
| 16 | +# | |
| 17 | + | |
| 18 | +APP_NAME := Zyquo Router | |
| 19 | +EXEC_NAME := ZyquoRouter | |
| 20 | +BUNDLE_ID := com.zyquo.router | |
| 21 | +VERSION := 1.0.0 | |
| 22 | +BUILD_NUM := 1 | |
| 23 | +MIN_MACOS := 13.0 | |
| 24 | +DIST := dist | |
| 25 | +APP_DIR := $(DIST)/$(APP_NAME).app | |
| 26 | +IDENTITY := Developer ID Application: Simon-Pierre Boucher (3YM54G49SN) | |
| 27 | +NOTARY_PROFILE:= MacLustr-Notarize | |
| 28 | +ENTITLEMENTS := Resources/ZyquoRouter.entitlements | |
| 29 | + | |
| 30 | +.PHONY: dev build bundle release universal icon test clean run | |
| 31 | + | |
| 32 | +dev: build bundle | |
| 33 | + @echo "=== Ad-hoc signing (dev) ===" | |
| 34 | + codesign --force --deep --sign - "$(APP_DIR)" | |
| 35 | + codesign --verify --deep --strict "$(APP_DIR)" | |
| 36 | + @echo "Packaged: $(APP_DIR) — launch with: open \"$(APP_DIR)\"" | |
| 37 | + | |
| 38 | +build: | |
| 39 | + swift build -c release | |
| 40 | + | |
| 41 | +test: | |
| 42 | + swift test | |
| 43 | + | |
| 44 | +run: dev | |
| 45 | + open "$(APP_DIR)" | |
| 46 | + | |
| 47 | +bundle: | |
| 48 | + @echo "=== Assembling $(APP_DIR) ===" | |
| 49 | + rm -rf "$(APP_DIR)" | |
| 50 | + mkdir -p "$(APP_DIR)/Contents/MacOS" "$(APP_DIR)/Contents/Resources" | |
| 51 | + cp .build/release/$(EXEC_NAME) "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)" | |
| 52 | + @for b in .build/release/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 53 | + @if [ -f Resources/AppIcon.icns ]; then cp Resources/AppIcon.icns "$(APP_DIR)/Contents/Resources/AppIcon.icns"; fi | |
| 54 | + @for r in Resources/MenuBarIcon.png Resources/MenuBarIcon@2x.png; do [ -f "$$r" ] && cp "$$r" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 55 | + scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)" | |
| 56 | + | |
| 57 | +# Both arch builds land in .build/<triple>/Release; each slice is copied aside | |
| 58 | +# before lipo so the second build cannot clobber the first. | |
| 59 | +universal: | |
| 60 | + @echo "=== Building universal binary (arm64 + x86_64) ===" | |
| 61 | + mkdir -p .build/universal | |
| 62 | + swift build -c release --arch arm64 | |
| 63 | + cp .build/arm64-apple-macosx/release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).arm64 2>/dev/null || \ | |
| 64 | + cp .build/out/Products/Release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).arm64 | |
| 65 | + swift build -c release --arch x86_64 | |
| 66 | + cp .build/x86_64-apple-macosx/release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).x86_64 2>/dev/null || \ | |
| 67 | + cp .build/out/Products/Release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).x86_64 | |
| 68 | + lipo -create \ | |
| 69 | + .build/universal/$(EXEC_NAME).arm64 \ | |
| 70 | + .build/universal/$(EXEC_NAME).x86_64 \ | |
| 71 | + -output .build/universal/$(EXEC_NAME) | |
| 72 | + lipo -archs .build/universal/$(EXEC_NAME) | |
| 73 | + | |
| 74 | +release: universal | |
| 75 | + @echo "=== Assembling $(APP_DIR) (universal) ===" | |
| 76 | + rm -rf "$(APP_DIR)" | |
| 77 | + mkdir -p "$(APP_DIR)/Contents/MacOS" "$(APP_DIR)/Contents/Resources" | |
| 78 | + cp .build/universal/$(EXEC_NAME) "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)" | |
| 79 | + @for b in .build/arm64-apple-macosx/release/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 80 | + @if [ -f Resources/AppIcon.icns ]; then cp Resources/AppIcon.icns "$(APP_DIR)/Contents/Resources/AppIcon.icns"; fi | |
| 81 | + @for r in Resources/MenuBarIcon.png Resources/MenuBarIcon@2x.png; do [ -f "$$r" ] && cp "$$r" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 82 | + scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)" | |
| 83 | + scripts/notarize.sh "$(APP_DIR)" "$(IDENTITY)" "$(NOTARY_PROFILE)" "$(ENTITLEMENTS)" | |
| 84 | + | |
| 85 | +icon: | |
| 86 | + scripts/generate-icon.sh | |
| 87 | + | |
| 88 | +clean: | |
| 89 | + rm -rf .build $(DIST) | |
added
Package.resolved
+176 −0
@@ -0,0 +1,176 @@ | ||
| 1 | +{ | |
| 2 | + "pins" : [ | |
| 3 | + { | |
| 4 | + "identity" : "swift-algorithms", | |
| 5 | + "kind" : "remoteSourceControl", | |
| 6 | + "location" : "https://github.com/apple/swift-algorithms.git", | |
| 7 | + "state" : { | |
| 8 | + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", | |
| 9 | + "version" : "1.2.1" | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "identity" : "swift-asn1", | |
| 14 | + "kind" : "remoteSourceControl", | |
| 15 | + "location" : "https://github.com/apple/swift-asn1.git", | |
| 16 | + "state" : { | |
| 17 | + "revision" : "a9a5efd40eaf558a2bcd48d64b1d1646be686008", | |
| 18 | + "version" : "1.7.1" | |
| 19 | + } | |
| 20 | + }, | |
| 21 | + { | |
| 22 | + "identity" : "swift-async-algorithms", | |
| 23 | + "kind" : "remoteSourceControl", | |
| 24 | + "location" : "https://github.com/apple/swift-async-algorithms.git", | |
| 25 | + "state" : { | |
| 26 | + "revision" : "3da39bbc4e687d4192af7c9cf4eab805745a0b9c", | |
| 27 | + "version" : "1.1.5" | |
| 28 | + } | |
| 29 | + }, | |
| 30 | + { | |
| 31 | + "identity" : "swift-atomics", | |
| 32 | + "kind" : "remoteSourceControl", | |
| 33 | + "location" : "https://github.com/apple/swift-atomics.git", | |
| 34 | + "state" : { | |
| 35 | + "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", | |
| 36 | + "version" : "1.3.1" | |
| 37 | + } | |
| 38 | + }, | |
| 39 | + { | |
| 40 | + "identity" : "swift-certificates", | |
| 41 | + "kind" : "remoteSourceControl", | |
| 42 | + "location" : "https://github.com/apple/swift-certificates.git", | |
| 43 | + "state" : { | |
| 44 | + "revision" : "449dbbecd0f31e82b510ada227ca152caa8b5e98", | |
| 45 | + "version" : "1.19.4" | |
| 46 | + } | |
| 47 | + }, | |
| 48 | + { | |
| 49 | + "identity" : "swift-cmark", | |
| 50 | + "kind" : "remoteSourceControl", | |
| 51 | + "location" : "https://github.com/swiftlang/swift-cmark.git", | |
| 52 | + "state" : { | |
| 53 | + "revision" : "924936d0427cb25a61169739a7660230bffa6ea6", | |
| 54 | + "version" : "0.8.0" | |
| 55 | + } | |
| 56 | + }, | |
| 57 | + { | |
| 58 | + "identity" : "swift-collections", | |
| 59 | + "kind" : "remoteSourceControl", | |
| 60 | + "location" : "https://github.com/apple/swift-collections.git", | |
| 61 | + "state" : { | |
| 62 | + "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", | |
| 63 | + "version" : "1.6.0" | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + { | |
| 67 | + "identity" : "swift-crypto", | |
| 68 | + "kind" : "remoteSourceControl", | |
| 69 | + "location" : "https://github.com/apple/swift-crypto.git", | |
| 70 | + "state" : { | |
| 71 | + "revision" : "47d3869a7291f085c1fb9fb1e6d3b97a793f45c6", | |
| 72 | + "version" : "4.5.1" | |
| 73 | + } | |
| 74 | + }, | |
| 75 | + { | |
| 76 | + "identity" : "swift-http-structured-headers", | |
| 77 | + "kind" : "remoteSourceControl", | |
| 78 | + "location" : "https://github.com/apple/swift-http-structured-headers.git", | |
| 79 | + "state" : { | |
| 80 | + "revision" : "933538faa42c432d385f02e07df0ace7c5ecfc47", | |
| 81 | + "version" : "1.7.0" | |
| 82 | + } | |
| 83 | + }, | |
| 84 | + { | |
| 85 | + "identity" : "swift-http-types", | |
| 86 | + "kind" : "remoteSourceControl", | |
| 87 | + "location" : "https://github.com/apple/swift-http-types.git", | |
| 88 | + "state" : { | |
| 89 | + "revision" : "db774a277f60063a32d854f2980299caf06da041", | |
| 90 | + "version" : "1.6.0" | |
| 91 | + } | |
| 92 | + }, | |
| 93 | + { | |
| 94 | + "identity" : "swift-log", | |
| 95 | + "kind" : "remoteSourceControl", | |
| 96 | + "location" : "https://github.com/apple/swift-log.git", | |
| 97 | + "state" : { | |
| 98 | + "revision" : "a878e7f8f46cfc0e1125e565b5c08e7d5272dc9a", | |
| 99 | + "version" : "1.14.0" | |
| 100 | + } | |
| 101 | + }, | |
| 102 | + { | |
| 103 | + "identity" : "swift-markdown", | |
| 104 | + "kind" : "remoteSourceControl", | |
| 105 | + "location" : "https://github.com/swiftlang/swift-markdown.git", | |
| 106 | + "state" : { | |
| 107 | + "revision" : "3c6f9523da3a1ec2fd829673e472d95b8097a3b8", | |
| 108 | + "version" : "0.8.0" | |
| 109 | + } | |
| 110 | + }, | |
| 111 | + { | |
| 112 | + "identity" : "swift-nio", | |
| 113 | + "kind" : "remoteSourceControl", | |
| 114 | + "location" : "https://github.com/apple/swift-nio.git", | |
| 115 | + "state" : { | |
| 116 | + "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b", | |
| 117 | + "version" : "2.101.3" | |
| 118 | + } | |
| 119 | + }, | |
| 120 | + { | |
| 121 | + "identity" : "swift-nio-extras", | |
| 122 | + "kind" : "remoteSourceControl", | |
| 123 | + "location" : "https://github.com/apple/swift-nio-extras.git", | |
| 124 | + "state" : { | |
| 125 | + "revision" : "88a51340f59cf181ebde888bd1b749296b3ec029", | |
| 126 | + "version" : "1.34.3" | |
| 127 | + } | |
| 128 | + }, | |
| 129 | + { | |
| 130 | + "identity" : "swift-nio-http2", | |
| 131 | + "kind" : "remoteSourceControl", | |
| 132 | + "location" : "https://github.com/apple/swift-nio-http2.git", | |
| 133 | + "state" : { | |
| 134 | + "revision" : "45bdf670248be5f16ec0340e125dca285536f0fb", | |
| 135 | + "version" : "1.45.0" | |
| 136 | + } | |
| 137 | + }, | |
| 138 | + { | |
| 139 | + "identity" : "swift-nio-ssl", | |
| 140 | + "kind" : "remoteSourceControl", | |
| 141 | + "location" : "https://github.com/apple/swift-nio-ssl.git", | |
| 142 | + "state" : { | |
| 143 | + "revision" : "d930168b86f46ca51a4bc09c5ca45c1833db8067", | |
| 144 | + "version" : "2.37.2" | |
| 145 | + } | |
| 146 | + }, | |
| 147 | + { | |
| 148 | + "identity" : "swift-numerics", | |
| 149 | + "kind" : "remoteSourceControl", | |
| 150 | + "location" : "https://github.com/apple/swift-numerics.git", | |
| 151 | + "state" : { | |
| 152 | + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", | |
| 153 | + "version" : "1.1.1" | |
| 154 | + } | |
| 155 | + }, | |
| 156 | + { | |
| 157 | + "identity" : "swift-service-lifecycle", | |
| 158 | + "kind" : "remoteSourceControl", | |
| 159 | + "location" : "https://github.com/swift-server/swift-service-lifecycle.git", | |
| 160 | + "state" : { | |
| 161 | + "revision" : "9829955b385e5bb88128b73f1b8389e9b9c3191a", | |
| 162 | + "version" : "2.11.0" | |
| 163 | + } | |
| 164 | + }, | |
| 165 | + { | |
| 166 | + "identity" : "swift-system", | |
| 167 | + "kind" : "remoteSourceControl", | |
| 168 | + "location" : "https://github.com/apple/swift-system.git", | |
| 169 | + "state" : { | |
| 170 | + "revision" : "50688cacbd41d547e9eb9f7a213542340b7c442b", | |
| 171 | + "version" : "1.7.5" | |
| 172 | + } | |
| 173 | + } | |
| 174 | + ], | |
| 175 | + "version" : 2 | |
| 176 | +} | |
added
Package.swift
+40 −0
@@ -0,0 +1,40 @@ | ||
| 1 | +// swift-tools-version:5.9 | |
| 2 | +// | |
| 3 | +// Package.swift | |
| 4 | +// Zyquo Router | |
| 5 | +// | |
| 6 | +// Author: Simon-Pierre Boucher | |
| 7 | +// Mail: contact@spboucher.ai | |
| 8 | +// | |
| 9 | + | |
| 10 | +import PackageDescription | |
| 11 | + | |
| 12 | +let package = Package( | |
| 13 | + name: "ZyquoRouter", | |
| 14 | + platforms: [ | |
| 15 | + .macOS(.v13) | |
| 16 | + ], | |
| 17 | + dependencies: [ | |
| 18 | + .package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"), | |
| 19 | + .package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.24.0"), | |
| 20 | + .package(url: "https://github.com/swiftlang/swift-markdown.git", from: "0.5.0") | |
| 21 | + ], | |
| 22 | + targets: [ | |
| 23 | + .executableTarget( | |
| 24 | + name: "ZyquoRouter", | |
| 25 | + dependencies: [ | |
| 26 | + .product(name: "NIOCore", package: "swift-nio"), | |
| 27 | + .product(name: "NIOPosix", package: "swift-nio"), | |
| 28 | + .product(name: "NIOHTTP1", package: "swift-nio"), | |
| 29 | + .product(name: "NIOExtras", package: "swift-nio-extras"), | |
| 30 | + .product(name: "Markdown", package: "swift-markdown") | |
| 31 | + ], | |
| 32 | + path: "Sources/ZyquoRouter" | |
| 33 | + ), | |
| 34 | + .testTarget( | |
| 35 | + name: "ZyquoRouterTests", | |
| 36 | + dependencies: ["ZyquoRouter"], | |
| 37 | + path: "Tests/ZyquoRouterTests" | |
| 38 | + ), | |
| 39 | + ] | |
| 40 | +) | |
added
Resources/ZyquoRouter.entitlements
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 3 | +<plist version="1.0"> | |
| 4 | +<dict> | |
| 5 | + <key>com.apple.security.cs.allow-jit</key> | |
| 6 | + <false/> | |
| 7 | +</dict> | |
| 8 | +</plist> | |
added
Sources/ZyquoRouter/App/Main.swift
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +// | |
| 2 | +// Main.swift | |
| 3 | +// Zyquo Router | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | +// Entry point. CLI modes (verification harness, vault seeding) hang off this | |
| 9 | +// in later phases; otherwise the SwiftUI app launches. | |
| 10 | +// | |
| 11 | +// Deliberately a synchronous main: launching NSApplicationMain from an async | |
| 12 | +// main() corrupts Swift concurrency's executor setup (background tasks stop | |
| 13 | +// being scheduled), so CLI modes must drive their async work explicitly. | |
| 14 | +// | |
| 15 | + | |
| 16 | +import Foundation | |
| 17 | + | |
| 18 | +@main | |
| 19 | +enum Main { | |
| 20 | + static func main() { | |
| 21 | + ZyquoRouterApp.main() | |
| 22 | + } | |
| 23 | +} | |
added
Sources/ZyquoRouter/App/ZyquoRouterApp.swift
+42 −0
@@ -0,0 +1,42 @@ | ||
| 1 | +// | |
| 2 | +// ZyquoRouterApp.swift | |
| 3 | +// Zyquo Router | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | + | |
| 9 | +import SwiftUI | |
| 10 | + | |
| 11 | +struct ZyquoRouterApp: App { | |
| 12 | + @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate | |
| 13 | + | |
| 14 | + var body: some Scene { | |
| 15 | + WindowGroup("Zyquo Router") { | |
| 16 | + PlaceholderView() | |
| 17 | + } | |
| 18 | + .defaultSize(width: 1280, height: 820) | |
| 19 | + } | |
| 20 | +} | |
| 21 | + | |
| 22 | +final class AppDelegate: NSObject, NSApplicationDelegate { | |
| 23 | + func applicationDidFinishLaunching(_ notification: Notification) { | |
| 24 | + // Ensure the app fronts correctly when launched outside Finder | |
| 25 | + // (e.g. `swift run` or `open` from a terminal during development). | |
| 26 | + NSApplication.shared.setActivationPolicy(.regular) | |
| 27 | + NSApplication.shared.activate(ignoringOtherApps: true) | |
| 28 | + } | |
| 29 | +} | |
| 30 | + | |
| 31 | +/// Phase 1 placeholder — replaced by the real dashboard in Phase 6. | |
| 32 | +private struct PlaceholderView: View { | |
| 33 | + var body: some View { | |
| 34 | + VStack(spacing: 8) { | |
| 35 | + Text("Zyquo Router") | |
| 36 | + .font(.largeTitle.weight(.semibold)) | |
| 37 | + Text("Gateway under construction — Phase 1") | |
| 38 | + .foregroundStyle(.secondary) | |
| 39 | + } | |
| 40 | + .frame(minWidth: 1020, minHeight: 660) | |
| 41 | + } | |
| 42 | +} | |
added
Tests/ZyquoRouterTests/ZyquoRouterTests.swift
+17 −0
@@ -0,0 +1,17 @@ | ||
| 1 | +// | |
| 2 | +// ZyquoRouterTests.swift | |
| 3 | +// Zyquo Router | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | +// Placeholder suite; translation-fixture tests land in Phase 3. | |
| 9 | +// | |
| 10 | + | |
| 11 | +import XCTest | |
| 12 | + | |
| 13 | +final class ZyquoRouterTests: XCTestCase { | |
| 14 | + func testScaffold() { | |
| 15 | + XCTAssertTrue(true) | |
| 16 | + } | |
| 17 | +} | |
modified
docs/PLAN.md
+20 −1
@@ -31,7 +31,26 @@ Keychain) and a file-by-file port plan; key caveats: Cloud has no native GeminiC | ||
| 31 | 31 | (Router translates Gemini natively per D10) and no tool-calling in Cloud's wire types |
| 32 | 32 | (Router extends them). All 12 real provider keys smoke-tested OK (HTTP 200). |
| 33 | 33 | |
| 34 | −## Phase 1 — Project setup (SPM, no Xcode IDE) — pending | |
| 34 | +## Phase 1 — Project setup (SPM, no Xcode IDE) | |
| 35 | + | |
| 36 | +- [x] 1.1 `Package.swift`: executable target `ZyquoRouter`, macOS 13+, deps = SwiftNIO (NIOCore/NIOPosix/NIOHTTP1/NIOExtras) + swift-markdown only | |
| 37 | +- [x] 1.2 `Makefile`: `make dev` (debug build + ad-hoc-signed `Zyquo Router.app` + run), `make build`, `make app` (release universal), `make clean`; release/notarize targets stubbed for Phase 8 | |
| 38 | +- [x] 1.3 `Resources/Info.plist`: `CFBundleDisplayName` = `Zyquo Router`, `com.zyquo.router`, `LSMinimumSystemVersion` 13.0, `NSHighResolutionCapable`, `LSApplicationCategoryType` developer-tools | |
| 39 | +- [x] 1.4 Entry point: `@main` SwiftUI `App` + AppDelegate activation from terminal; placeholder window | |
| 40 | +- [x] 1.5 `swift build` clean, zero warnings; `make dev` launches the app window | |
| 41 | +- [x] 1.6 Mandatory file headers on every code file created | |
| 42 | + | |
| 43 | +**Phase gate: PASSED (2026-07-30).** | |
| 44 | + | |
| 45 | +**Phase 1 summary:** SPM executable `ZyquoRouter` (macOS 13+, deps: SwiftNIO 2 + | |
| 46 | +NIOExtras + swift-markdown) builds with zero warnings on the Xcode 6.3.3 toolchain — | |
| 47 | +no SDKROOT pin needed here, unlike Cloud's CLT-only machine (noted in the Makefile). | |
| 48 | +`make dev` assembles `dist/Zyquo Router.app` (com.zyquo.router, developer-tools | |
| 49 | +category, ad-hoc signed, plist lints OK); the placeholder SwiftUI window launches | |
| 50 | +from terminal, activates, and quits cleanly. Test target scaffolded and green. | |
| 51 | +Family conventions ported from Zyquo Cloud's Makefile/write-info-plist.sh, incl. | |
| 52 | +the Developer ID identity + notary profile constants for Phase 8. | |
| 53 | + | |
| 35 | 54 | ## Phase 2 — Architecture + server skeleton — pending |
| 36 | 55 | ## Phase 3 — Standardized API (the core) — pending |
| 37 | 56 | ## Phase 4 — Design system & UI spec — pending |
added
scripts/write-info-plist.sh
+57 −0
@@ -0,0 +1,57 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# | |
| 3 | +# write-info-plist.sh | |
| 4 | +# Zyquo Router | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Mail: contact@spboucher.ai | |
| 8 | +# | |
| 9 | +# Writes Contents/Info.plist into an assembled app bundle. | |
| 10 | +# Usage: write-info-plist.sh <app-dir> <app-name> <exec-name> <bundle-id> <version> <build> <min-macos> | |
| 11 | +# | |
| 12 | +set -euo pipefail | |
| 13 | + | |
| 14 | +APP_DIR="$1"; APP_NAME="$2"; EXEC_NAME="$3"; BUNDLE_ID="$4"; VERSION="$5"; BUILD_NUM="$6"; MIN_MACOS="$7" | |
| 15 | + | |
| 16 | +cat > "$APP_DIR/Contents/Info.plist" << PLIST | |
| 17 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 18 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 19 | +<plist version="1.0"> | |
| 20 | +<dict> | |
| 21 | + <key>CFBundleName</key> | |
| 22 | + <string>${APP_NAME}</string> | |
| 23 | + <key>CFBundleDisplayName</key> | |
| 24 | + <string>${APP_NAME}</string> | |
| 25 | + <key>CFBundleIdentifier</key> | |
| 26 | + <string>${BUNDLE_ID}</string> | |
| 27 | + <key>CFBundleShortVersionString</key> | |
| 28 | + <string>${VERSION}</string> | |
| 29 | + <key>CFBundleVersion</key> | |
| 30 | + <string>${BUILD_NUM}</string> | |
| 31 | + <key>CFBundleExecutable</key> | |
| 32 | + <string>${EXEC_NAME}</string> | |
| 33 | + <key>CFBundlePackageType</key> | |
| 34 | + <string>APPL</string> | |
| 35 | + <key>CFBundleIconFile</key> | |
| 36 | + <string>AppIcon</string> | |
| 37 | + <key>CFBundleInfoDictionaryVersion</key> | |
| 38 | + <string>6.0</string> | |
| 39 | + <key>LSMinimumSystemVersion</key> | |
| 40 | + <string>${MIN_MACOS}</string> | |
| 41 | + <key>NSHighResolutionCapable</key> | |
| 42 | + <true/> | |
| 43 | + <key>NSPrincipalClass</key> | |
| 44 | + <string>NSApplication</string> | |
| 45 | + <key>LSApplicationCategoryType</key> | |
| 46 | + <string>public.app-category.developer-tools</string> | |
| 47 | + <key>NSHumanReadableCopyright</key> | |
| 48 | + <string>© 2026 Simon-Pierre Boucher. All rights reserved.</string> | |
| 49 | + <key>NSSupportsAutomaticTermination</key> | |
| 50 | + <false/> | |
| 51 | + <key>NSSupportsSuddenTermination</key> | |
| 52 | + <false/> | |
| 53 | +</dict> | |
| 54 | +</plist> | |
| 55 | +PLIST | |
| 56 | + | |
| 57 | +echo "Info.plist written to $APP_DIR/Contents/Info.plist" | |
| 58 | ||