diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 2f27efa..f674259 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -10,7 +10,7 @@ body: id: version attributes: label: T4 Code version - placeholder: "0.1.26" + placeholder: "0.1.27" validations: required: true - type: dropdown diff --git a/README.md b/README.md index f428938..c530575 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ T4 Code is a free, open-source (MIT) desktop app for [Oh My Pi](https://github.c ![T4 Code main window](docs/assets/t4-code-main.png) -[**Download v0.1.26**](https://github.com/LycaonLLC/t4-code/releases/tag/v0.1.26) · [**Docs**](https://t4code.net/docs) · [**Get the source**](#build-from-source) +[**Download v0.1.27**](https://github.com/LycaonLLC/t4-code/releases/tag/v0.1.27) · [**Docs**](https://t4code.net/docs) · [**Get the source**](#build-from-source) ## Requirements -T4 Code needs an OMP build with desktop appserver support. For v0.1.26, use the public integration build below. +T4 Code needs an OMP build with desktop appserver support. For v0.1.27, use the public integration build below. -T4 Code v0.1.26 was verified with OMP 17.0.5 built from [`772e5e41`](https://github.com/lyc-aon/oh-my-pi/commit/772e5e41eb1537177349247add96a851721c5bfa), tagged [`t4code-17.0.5-appserver-5`](https://github.com/lyc-aon/oh-my-pi/tree/t4code-17.0.5-appserver-5). That public integration is based on the official upstream [`v17.0.5`](https://github.com/can1357/oh-my-pi/tree/v17.0.5) tag at [`9fd6e971`](https://github.com/can1357/oh-my-pi/commit/9fd6e97113f5ed3a847e66d346970efdf8afcad9). It adds faster appserver startup, cross-session attention and transcript search, the negotiated browser-preview command surface, redacted Codex transport diagnostics, the versioned Agent View lifecycle contract, session-owned cancellation, macOS system-temp aliases, workspace-native build artifacts, retry-safe release metadata, lock-aware session observation, complete transcript reconciliation, missing-lock-only promotion, the cooperative `/continue-in-t4` handoff, and deterministic session ordering. Fork CI verifies the exact upstream base, ancestry, release gates, and published binaries. The official upstream v17.0.5 tag has no `appserver` command, so it cannot host T4 Code. The verified runtime is a normal build from the public `lyc-aon/oh-my-pi` source. T4 Code vendors `@oh-my-pi/app-wire` 0.6.1 from integration commit [`e3e15c03`](https://github.com/lyc-aon/oh-my-pi/commit/e3e15c03ae95ebbda5f26495cd21213cc53518b1), source tree `e0f32b279eb4b8cbc403e47d765a226bee99c99f`. +T4 Code v0.1.27 was verified with OMP 17.0.5 built from [`772e5e41`](https://github.com/lyc-aon/oh-my-pi/commit/772e5e41eb1537177349247add96a851721c5bfa), tagged [`t4code-17.0.5-appserver-5`](https://github.com/lyc-aon/oh-my-pi/tree/t4code-17.0.5-appserver-5). That public integration is based on the official upstream [`v17.0.5`](https://github.com/can1357/oh-my-pi/tree/v17.0.5) tag at [`9fd6e971`](https://github.com/can1357/oh-my-pi/commit/9fd6e97113f5ed3a847e66d346970efdf8afcad9). It adds faster appserver startup, cross-session attention and transcript search, the negotiated browser-preview command surface, redacted Codex transport diagnostics, the versioned Agent View lifecycle contract, session-owned cancellation, macOS system-temp aliases, workspace-native build artifacts, retry-safe release metadata, lock-aware session observation, complete transcript reconciliation, missing-lock-only promotion, the cooperative `/continue-in-t4` handoff, and deterministic session ordering. Fork CI verifies the exact upstream base, ancestry, release gates, and published binaries. The official upstream v17.0.5 tag has no `appserver` command, so it cannot host T4 Code. The verified runtime is a normal build from the public `lyc-aon/oh-my-pi` source. T4 Code vendors `@oh-my-pi/app-wire` 0.6.1 from integration commit [`e3e15c03`](https://github.com/lyc-aon/oh-my-pi/commit/e3e15c03ae95ebbda5f26495cd21213cc53518b1), source tree `e0f32b279eb4b8cbc403e47d765a226bee99c99f`. The current source tree advances the vendored contract to `@oh-my-pi/app-wire` 0.6.1 from integration commit [`e3e15c03`](https://github.com/lyc-aon/oh-my-pi/commit/e3e15c03ae95ebbda5f26495cd21213cc53518b1), source tree `e0f32b279eb4b8cbc403e47d765a226bee99c99f`. It supplies bounded cross-session transcript search, historical context, and the browser-preview wire contract. @@ -20,10 +20,11 @@ The current source tree advances the vendored contract to `@oh-my-pi/app-wire` 0 | Linux | x86_64 | `.deb`, AppImage | | macOS | Apple Silicon (arm64) | `.dmg`, `.zip` (**signed and notarized**) | -No Windows build and no Intel Mac build in v0.1.26. The iOS TestFlight build is coming soon. +No Windows build and no Intel Mac build in v0.1.27. The iOS TestFlight build is coming soon. -## What changed in v0.1.26 +## What changed in v0.1.27 +- Mac packaging now waits for code signing to finish before Apple notarization begins. - Signed Mac builds now start their bundled OMP backend correctly. Only the OMP executable receives permission to load OMP's native module; the T4 Code app and Electron helpers keep normal library validation enabled. - The app verifies the backend's exact Developer ID certificate before copying the signed bytes, while packaging still verifies the original public OMP download hash. - macOS downloads remain signed with the project's pinned Developer ID identity, notarized by Apple, stapled, and checked by Gatekeeper before publication. @@ -48,7 +49,7 @@ No Windows build and no Intel Mac build in v0.1.26. The iOS TestFlight build is ### Android 1. On the Android phone, sign in to Tailscale with an account that can reach the T4 Code host. -2. Download [`T4-Code-0.1.26-android.apk`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-android.apk). +2. Download [`T4-Code-0.1.27-android.apk`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-android.apk). 3. If Android asks, allow your browser or file manager to install unknown apps, then install the APK. 4. Open T4 Code and enter the host's HTTPS Tailscale address, including its port. The app saves the address; you can add more hosts later and switch between them. @@ -57,8 +58,8 @@ The APK does not contain an appserver or expose one to the public internet. It c ### Linux (Debian/Ubuntu) ```sh -wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-linux-amd64.deb -sudo apt install ./T4-Code-0.1.26-linux-amd64.deb +wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-linux-amd64.deb +sudo apt install ./T4-Code-0.1.27-linux-amd64.deb ``` Use `apt install` rather than `dpkg -i` so system dependencies resolve automatically. @@ -66,14 +67,14 @@ Use `apt install` rather than `dpkg -i` so system dependencies resolve automatic ### Linux (AppImage) ```sh -wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-linux-x86_64.AppImage -chmod +x T4-Code-0.1.26-linux-x86_64.AppImage -./T4-Code-0.1.26-linux-x86_64.AppImage +wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-linux-x86_64.AppImage +chmod +x T4-Code-0.1.27-linux-x86_64.AppImage +./T4-Code-0.1.27-linux-x86_64.AppImage ``` ### macOS (Apple Silicon) -1. Download [`T4-Code-0.1.26-mac-arm64.dmg`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-mac-arm64.dmg) (or [`T4-Code-0.1.26-mac-arm64.zip`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-mac-arm64.zip)). +1. Download [`T4-Code-0.1.27-mac-arm64.dmg`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-mac-arm64.dmg) (or [`T4-Code-0.1.27-mac-arm64.zip`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-mac-arm64.zip)). 2. Drag `T4 Code.app` into `/Applications`. 3. Open T4 Code normally. The release workflow verifies the pinned publisher, hardened runtime, secure timestamp, Apple notarization, stapled ticket, and Gatekeeper acceptance before publication. diff --git a/SECURITY.md b/SECURITY.md index a61b01b..3d90860 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -26,5 +26,5 @@ We read every report and will reply to tell you what happens next. This is a sma - T4 Code is a desktop client. The OMP runtime is a separate project; runtime vulnerabilities belong at . - Pairing credentials are encrypted with the OS keychain via Electron `safeStorage`. Reports about credential handling, the pairing flow, or the `t4-code://` deep-link handler are especially welcome. -- The macOS v0.1.26 build is signed with Apple Developer ID and notarized by Apple. Reports of certificate, Team ID, hardened-runtime, timestamp, Gatekeeper, or stapled-ticket drift are security-relevant. +- The macOS v0.1.27 build is signed with Apple Developer ID and notarized by Apple. Reports of certificate, Team ID, hardened-runtime, timestamp, Gatekeeper, or stapled-ticket drift are security-relevant. - Starting with v0.1.24, the release workflow requires the pinned Developer ID identity, hardened runtime, Apple notarization, a stapled ticket, and a successful Gatekeeper assessment before publishing macOS artifacts. diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e0edc90..54103b6 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/desktop", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "main": "dist-electron/main.cjs", diff --git a/apps/desktop/src/target-manager.ts b/apps/desktop/src/target-manager.ts index 845f798..13705ec 100644 --- a/apps/desktop/src/target-manager.ts +++ b/apps/desktop/src/target-manager.ts @@ -383,7 +383,7 @@ export class DesktopTargetManager { capabilities: requestedCapabilities, requestedFeatures: REQUESTED_FEATURES, compatibilityRequestedFeatures: COMPATIBILITY_FEATURES, - client: { name: "T4 Code", version: "0.1.26", build: "desktop", platform: process.platform }, + client: { name: "T4 Code", version: "0.1.27", build: "desktop", platform: process.platform }, reconnect: { baseMs: 250, maxMs: 10_000 }, }; const client = createOmpClient(clientOptions); diff --git a/apps/mobile/capacitor.config.json b/apps/mobile/capacitor.config.json index 7a1b395..f30a0b5 100644 --- a/apps/mobile/capacitor.config.json +++ b/apps/mobile/capacitor.config.json @@ -3,7 +3,7 @@ "appName": "T4 Code", "webDir": "dist", "loggingBehavior": "debug", - "appendUserAgent": " T4CodeMobile/0.1.26", + "appendUserAgent": " T4CodeMobile/0.1.27", "android": { "path": "android", "minWebViewVersion": 60, diff --git a/apps/mobile/package.json b/apps/mobile/package.json index a08d1b8..c9f318a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/mobile", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "scripts": { diff --git a/apps/site/package.json b/apps/site/package.json index e834fea..557129b 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/site", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "scripts": { diff --git a/apps/site/src/release.ts b/apps/site/src/release.ts index ca9671d..3af8a68 100644 --- a/apps/site/src/release.ts +++ b/apps/site/src/release.ts @@ -13,8 +13,8 @@ export const OMP_UPSTREAM_TAG = "v17.0.5"; export const OMP_UPSTREAM_COMMIT = "9fd6e97113f5ed3a847e66d346970efdf8afcad9"; export const OMP_UPSTREAM_URL = `${OMP_URL}/tree/${OMP_UPSTREAM_TAG}`; export const APP_WIRE_VERSION = "0.6.1"; -export const RELEASE_TAG = "v0.1.26"; -export const RELEASE_VERSION = "0.1.26"; +export const RELEASE_TAG = "v0.1.27"; +export const RELEASE_VERSION = "0.1.27"; export const RELEASES_URL = `${REPO_URL}/releases/tag/${RELEASE_TAG}`; export const RELEASE_MANIFEST_URL = `${SITE_URL}/releases/latest.json`; @@ -49,11 +49,11 @@ function asset( } export const RELEASE_ASSETS: readonly ReleaseAsset[] = [ - asset("android", "apk", "universal", "T4-Code-0.1.26-android.apk", "Android APK"), - asset("linux", "deb", "x86_64", "T4-Code-0.1.26-linux-amd64.deb", "Linux .deb"), - asset("linux", "appimage", "x86_64", "T4-Code-0.1.26-linux-x86_64.AppImage", "Linux AppImage"), - asset("mac", "dmg", "arm64", "T4-Code-0.1.26-mac-arm64.dmg", "macOS .dmg"), - asset("mac", "zip", "arm64", "T4-Code-0.1.26-mac-arm64.zip", "macOS .zip"), + asset("android", "apk", "universal", "T4-Code-0.1.27-android.apk", "Android APK"), + asset("linux", "deb", "x86_64", "T4-Code-0.1.27-linux-amd64.deb", "Linux .deb"), + asset("linux", "appimage", "x86_64", "T4-Code-0.1.27-linux-x86_64.AppImage", "Linux AppImage"), + asset("mac", "dmg", "arm64", "T4-Code-0.1.27-mac-arm64.dmg", "macOS .dmg"), + asset("mac", "zip", "arm64", "T4-Code-0.1.27-mac-arm64.zip", "macOS .zip"), ]; export function assetsFor(platform: Platform): readonly ReleaseAsset[] { diff --git a/apps/site/test/release.test.ts b/apps/site/test/release.test.ts index 17e524c..94bf47c 100644 --- a/apps/site/test/release.test.ts +++ b/apps/site/test/release.test.ts @@ -1,4 +1,4 @@ -// Release contract guard: exact v0.1.26 asset names and URLs, and the +// Release contract guard: exact v0.1.27 asset names and URLs, and the // platform-detection rule the hero download button relies on. import { describe, expect, it } from "vite-plus/test"; import { @@ -20,13 +20,13 @@ import { } from "../src/release.ts"; describe("release assets", () => { - it("carries the five contracted v0.1.26 filenames", () => { + it("carries the five contracted v0.1.27 filenames", () => { expect(RELEASE_ASSETS.map((a) => a.filename)).toEqual([ - "T4-Code-0.1.26-android.apk", - "T4-Code-0.1.26-linux-amd64.deb", - "T4-Code-0.1.26-linux-x86_64.AppImage", - "T4-Code-0.1.26-mac-arm64.dmg", - "T4-Code-0.1.26-mac-arm64.zip", + "T4-Code-0.1.27-android.apk", + "T4-Code-0.1.27-linux-amd64.deb", + "T4-Code-0.1.27-linux-x86_64.AppImage", + "T4-Code-0.1.27-mac-arm64.dmg", + "T4-Code-0.1.27-mac-arm64.zip", ]); }); @@ -38,8 +38,8 @@ describe("release assets", () => { it("targets the public LycaonLLC repo", () => { expect(REPO_URL).toBe("https://github.com/LycaonLLC/t4-code"); - expect(RELEASE_TAG).toBe("v0.1.26"); - expect(RELEASE_VERSION).toBe("0.1.26"); + expect(RELEASE_TAG).toBe("v0.1.27"); + expect(RELEASE_VERSION).toBe("0.1.27"); expect(RELEASE_MANIFEST_URL).toBe("https://t4code.net/releases/latest.json"); }); diff --git a/apps/web/package.json b/apps/web/package.json index 67057f2..b490117 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/web", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "scripts": { diff --git a/apps/web/src/platform/browser-shell-port.ts b/apps/web/src/platform/browser-shell-port.ts index b240e64..2b34753 100644 --- a/apps/web/src/platform/browser-shell-port.ts +++ b/apps/web/src/platform/browser-shell-port.ts @@ -305,7 +305,7 @@ export function createBrowserShellPort( }, client: { name: "T4 Code", - version: "0.1.26", + version: "0.1.27", build: mobilePlatform ?? "browser", platform: mobilePlatform ?? (platform === "darwin" ? "darwin" : "linux"), }, diff --git a/compat/omp-app-matrix.json b/compat/omp-app-matrix.json index 395ba4b..c699bdd 100644 --- a/compat/omp-app-matrix.json +++ b/compat/omp-app-matrix.json @@ -181,6 +181,6 @@ }, "desktop": { "package": "@t4-code/protocol", - "version": "0.1.26" + "version": "0.1.27" } } diff --git a/docs/CURRENT_RELEASE_NOTES.md b/docs/CURRENT_RELEASE_NOTES.md index cb7dfb1..5afe4e7 100644 --- a/docs/CURRENT_RELEASE_NOTES.md +++ b/docs/CURRENT_RELEASE_NOTES.md @@ -1,6 +1,8 @@ ## Signed Mac backend startup -T4 Code v0.1.26 lets the signed, bundled OMP backend load OMP's native module on macOS. The permission is applied only to the OMP executable inside the app. The top-level T4 Code app and its Electron helpers keep normal library validation enabled. +T4 Code v0.1.27 completes the signed Mac backend fix. Packaging now waits for the Promise-based signer to finish before notarization begins. The signed, bundled OMP backend can load OMP's native module, with that permission applied only to the OMP executable inside the app. The top-level T4 Code app and its Electron helpers keep normal library validation enabled. + +The v0.1.26 tag did not publish release files: its Mac job stopped when notarization detected that the legacy callback signer had returned before signing finished. No partial v0.1.26 GitHub Release was published. The protected release job verifies this boundary in both the DMG and ZIP before publication. It also checks the original OMP download's pinned size and SHA-256 hash, the project's exact Developer ID certificate and Team ID, hardened runtime, secure timestamp, stapled notarization ticket, and Gatekeeper result. Signing secrets are never bundled into the app. @@ -26,7 +28,7 @@ Session-linked browser previews now open in a dedicated workspace. The client pr ## Runtime provenance -T4 Code v0.1.26 vendors app-wire 0.6.1 from integration commit [e3e15c03](https://github.com/lyc-aon/oh-my-pi/commit/e3e15c03ae95ebbda5f26495cd21213cc53518b1), source tree `e0f32b279eb4b8cbc403e47d765a226bee99c99f`. The client contract remains `omp-app/1`. +T4 Code v0.1.27 vendors app-wire 0.6.1 from integration commit [e3e15c03](https://github.com/lyc-aon/oh-my-pi/commit/e3e15c03ae95ebbda5f26495cd21213cc53518b1), source tree `e0f32b279eb4b8cbc403e47d765a226bee99c99f`. The client contract remains `omp-app/1`. The verified OMP 17.0.5 runtime is built from commit [772e5e41](https://github.com/lyc-aon/oh-my-pi/commit/772e5e41eb1537177349247add96a851721c5bfa) and tagged [t4code-17.0.5-appserver-5](https://github.com/lyc-aon/oh-my-pi/tree/t4code-17.0.5-appserver-5). It provides the appserver used by the desktop and remote workflows, including faster startup, cross-session attention and transcript search, and the complete negotiated browser-preview command surface. Unsupported optional capabilities remain hidden when the host does not advertise them. diff --git a/e2e/site-mobile-docs.spec.ts b/e2e/site-mobile-docs.spec.ts index 46f188e..9752322 100644 --- a/e2e/site-mobile-docs.spec.ts +++ b/e2e/site-mobile-docs.spec.ts @@ -130,7 +130,7 @@ test("offers the Android APK without hiding desktop downloads", async ({ page }) await expect(androidDownload).toBeVisible(); await expect(androidDownload).toHaveAttribute( "href", - "https://github.com/LycaonLLC/t4-code/releases/download/v0.1.26/T4-Code-0.1.26-android.apk", + "https://github.com/LycaonLLC/t4-code/releases/download/v0.1.27/T4-Code-0.1.27-android.apk", ); await expect(page.getByRole("link", { name: /Linux/u }).first()).toBeVisible(); await expect(page.getByRole("link", { name: /macOS/u }).first()).toBeVisible(); diff --git a/package.json b/package.json index 2dc07a3..0cd0284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/root", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "scripts": { diff --git a/packages/client/package.json b/packages/client/package.json index d716bac..0f6d8f5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/client", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/packages/client/src/omp-client-frames.ts b/packages/client/src/omp-client-frames.ts index b24e0df..e7f81bb 100644 --- a/packages/client/src/omp-client-frames.ts +++ b/packages/client/src/omp-client-frames.ts @@ -84,7 +84,7 @@ export function sendClientHello( } catch { fatal(); return; } const encoded = encodeOutgoingMessage(provider, { kind: "hello", - client: options.client ?? { name: "t4-code", version: "0.1.26", build: "client", platform: "electron" }, + client: options.client ?? { name: "t4-code", version: "0.1.27", build: "client", platform: "electron" }, requestedFeatures: [...(options.requestedFeatures ?? ["resume"])], savedCursors, ...(options.capabilities === undefined ? {} : { capabilities: options.capabilities }), diff --git a/packages/fixture-server/package.json b/packages/fixture-server/package.json index c4c4131..acb28dd 100644 --- a/packages/fixture-server/package.json +++ b/packages/fixture-server/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/fixture-server", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/packages/protocol/package.json b/packages/protocol/package.json index 0179b59..ebdf23c 100644 --- a/packages/protocol/package.json +++ b/packages/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/protocol", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/packages/protocol/test/fixtures/platform-boundaries.ts b/packages/protocol/test/fixtures/platform-boundaries.ts index ebf054f..e74ef41 100644 --- a/packages/protocol/test/fixtures/platform-boundaries.ts +++ b/packages/protocol/test/fixtures/platform-boundaries.ts @@ -25,7 +25,7 @@ export const androidUpdateFixtures = Object.freeze({ }, { currentVersion: "0.1.22", - latestVersion: "0.1.26", + latestVersion: "0.1.27", checkedAt: 1_721_234_567_890, phase: "available", revision: 7, @@ -33,7 +33,7 @@ export const androidUpdateFixtures = Object.freeze({ }, { currentVersion: "0.1.22", - latestVersion: "0.1.26", + latestVersion: "0.1.27", phase: "installer", revision: 8, message: "Installer opened.\nReview Android's prompt.", diff --git a/packages/remote/package.json b/packages/remote/package.json index 76d6df5..93ac0ac 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/remote", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/packages/service-manager/package.json b/packages/service-manager/package.json index cf0791f..373d437 100644 --- a/packages/service-manager/package.json +++ b/packages/service-manager/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/service-manager", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 4c91d5d..b2364eb 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@t4-code/ui", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "exports": { diff --git a/scripts/check-release-consistency.test.mjs b/scripts/check-release-consistency.test.mjs index 729ebfe..daa3e6e 100644 --- a/scripts/check-release-consistency.test.mjs +++ b/scripts/check-release-consistency.test.mjs @@ -89,7 +89,7 @@ test("keeps verified and published runtime records aligned after promotion", () test("rejects a tag that differs from the package version", () => { assert.ok( collectReleaseConsistencyErrors(files, "v9.9.9").some((error) => - error.includes("release tag v9.9.9 does not match v0.1.26"), + error.includes("release tag v9.9.9 does not match v0.1.27"), ), ); }); @@ -118,7 +118,7 @@ test("tagged releases reject published provenance drift", () => { for (const [field, mutate] of appWireCases) { const drifted = changedRuntime("publishedAppWire", mutate); assert.ok( - collectReleaseConsistencyErrors(drifted, "v0.1.26").some((error) => + collectReleaseConsistencyErrors(drifted, "v0.1.27").some((error) => error.includes( `published app-wire ${field} must match current app-wire for tagged releases`, ), @@ -161,7 +161,7 @@ test("tagged releases reject published provenance drift", () => { for (const [field, mutate] of runtimeCases) { const drifted = changedRuntime("publishedRuntime", mutate); assert.ok( - collectReleaseConsistencyErrors(drifted, "v0.1.26").some((error) => + collectReleaseConsistencyErrors(drifted, "v0.1.27").some((error) => error.includes( `published runtime ${field} must match current verified runtime for tagged releases`, ), @@ -173,7 +173,7 @@ test("tagged releases reject published provenance drift", () => { runtime.artifactSha256 = "0".repeat(64); }); assert.ok( - collectReleaseConsistencyErrors(extended, "v0.1.26").some((error) => + collectReleaseConsistencyErrors(extended, "v0.1.27").some((error) => error.includes( "published runtime must exactly match current verified runtime for tagged releases", ), @@ -183,15 +183,15 @@ test("tagged releases reject published provenance drift", () => { test("rejects workspace, site, README, and runtime version drift", () => { const cases = [ - ["apps/web/package.json", (text) => text.replace('"version": "0.1.26"', '"version": "0.1.3"')], + ["apps/web/package.json", (text) => text.replace('"version": "0.1.27"', '"version": "0.1.3"')], [ "apps/site/src/release.ts", - (text) => text.replace('RELEASE_TAG = "v0.1.26"', 'RELEASE_TAG = "v0.1.3"'), + (text) => text.replace('RELEASE_TAG = "v0.1.27"', 'RELEASE_TAG = "v0.1.3"'), ], - ["README.md", (text) => text.replace("Download v0.1.26", "Download v0.1.3")], + ["README.md", (text) => text.replace("Download v0.1.27", "Download v0.1.3")], [ "apps/desktop/src/target-manager.ts", - (text) => text.replace('version: "0.1.26"', 'version: "0.1.3"'), + (text) => text.replace('version: "0.1.27"', 'version: "0.1.3"'), ], [ "apps/site/src/docs/content.ts", @@ -461,7 +461,7 @@ test("rejects stale README release URLs while allowing historical prose", () => const staleLink = changed("README.md", (text) => `${text}\n[Old release](${oldReleaseUrl})\n`); assert.ok( collectReleaseConsistencyErrors(staleLink).some((error) => - error.includes("release URL for v0.1.3; expected v0.1.26"), + error.includes("release URL for v0.1.3; expected v0.1.27"), ), ); assert.deepEqual(collectReleaseConsistencyErrors(files), []); diff --git a/scripts/packaging.test.mjs b/scripts/packaging.test.mjs index 1395255..9f750fb 100644 --- a/scripts/packaging.test.mjs +++ b/scripts/packaging.test.mjs @@ -8,6 +8,7 @@ import { validateMacosIdentityContract } from "./inspect-macos-release.mjs"; import { createT4MacOptionsForFile, isBundledOmpRuntime, + macSigner, normalizeMacSignOptions, } from "./sign-macos.mjs"; import { createPackage } from "@electron/asar"; @@ -151,6 +152,12 @@ test("macOS signing accepts current and legacy electron-builder callback shapes" assert.throws(() => normalizeMacSignOptions({}), /did not provide an application path/u); }); +test("macOS signing uses the Promise API that electron-builder can await", async () => { + const pending = macSigner({}); + assert.equal(typeof pending?.then, "function"); + await assert.rejects(pending); +}); + test("Android release identity is public, pinned, and wired into the release workflow", () => { assert.doesNotThrow(() => validateIdentityContract(androidIdentity)); assert.equal(androidIdentity.applicationId, "com.lycaonsolutions.t4code"); diff --git a/scripts/sign-macos.mjs b/scripts/sign-macos.mjs index b00ab35..17e7d33 100644 --- a/scripts/sign-macos.mjs +++ b/scripts/sign-macos.mjs @@ -1,6 +1,9 @@ -import { sign } from "@electron/osx-sign"; +// `sign` is the deprecated callback API and returns before signing finishes. +// electron-builder needs this Promise API so notarization cannot race signing. +import { signAsync } from "@electron/osx-sign"; export const OMP_RUNTIME_ENTITLEMENTS = "apps/desktop/build/entitlements.omp-runtime.plist"; +export const macSigner = signAsync; export function isBundledOmpRuntime(filePath) { return /[/\\]Contents[/\\]Resources[/\\]runtime[/\\]omp$/u.test(filePath); @@ -24,7 +27,7 @@ export function normalizeMacSignOptions(input) { export default async function signT4MacApp(input) { const options = normalizeMacSignOptions(input); - await sign({ + await macSigner({ ...options, optionsForFile: createT4MacOptionsForFile(options.optionsForFile), });