fix(companion): make reference-app exports reproducible by default

No diagram change needed: this change only makes generated reference-app timestamps deterministic and adds an override flag.
This commit is contained in:
William Valentin
2026-02-26 20:58:31 -08:00
parent 078c3799ce
commit 820af97859
10 changed files with 25 additions and 8 deletions
+1 -1
View File
@@ -1752,7 +1752,7 @@ Minimal companion CLI:
- `flynn companion --platform ios --export-shell-template ./dist/companion-ios-template` writes a platform-native starter template directory (`companion.bootstrap.json`, bootstrap/runtime starter files, `README.md`) and exits.
- `flynn companion --verify-release-bundle ./dist/companion-macos --verify-signing-key ./keys/release-public.pem --verify-signing-key-id team-k1 --require-signature` verifies checksums and signature metadata before install.
- `pnpm companion:bundle -- --output ./dist/companion-macos --platform macos --signing-key ./keys/release-private.pem --signing-key-id team-k1` builds and verifies a release bundle in one step.
- `pnpm companion:reference-apps -- --output ./apps/companion` regenerates macOS/iOS/Android reference app starter shells plus `apps/companion/macos-app` runnable scaffold.
- `pnpm companion:reference-apps -- --output ./apps/companion` regenerates macOS/iOS/Android reference app starter shells plus `apps/companion/macos-app` runnable scaffold using a reproducible default `generatedAt` timestamp (override with `--generated-at <iso>`).
- GitHub Actions workflow `.github/workflows/companion-release-bundle.yml` runs build-and-verify bundle automation and uploads release artifacts on manual dispatch.
`run-companion.sh` verifies bundle checksums (`CHECKSUMS.sha256`) before launching `flynn companion`.
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"generatedAt": "2026-02-27T04:56:19.684Z",
"generatedAt": "2026-02-27T00:00:00.000Z",
"gateway": {
"url": "ws://127.0.0.1:18800"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"generatedAt": "2026-02-27T04:56:19.684Z",
"generatedAt": "2026-02-27T00:00:00.000Z",
"gateway": {
"url": "ws://127.0.0.1:18800"
},
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"generatedAt": "2026-02-27T04:56:19.684Z",
"generatedAt": "2026-02-27T00:00:00.000Z",
"gateway": {
"url": "ws://127.0.0.1:18800"
},
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"generatedAt": "2026-02-27T04:56:19.684Z",
"generatedAt": "2026-02-27T00:00:00.000Z",
"gateway": {
"url": "ws://127.0.0.1:18800"
},
@@ -56,6 +56,7 @@ pnpm companion:reference-apps -- --output ./apps/companion
```
This also regenerates `apps/companion/macos-app`, a runnable Swift Package menu-bar reference app scaffold.
By default it uses a reproducible `generatedAt` timestamp (`2026-02-27T00:00:00.000Z`); pass `--generated-at <iso>` if you need a different value.
CI automation:
@@ -51,7 +51,7 @@ Within 8-10 weeks, ship a stable "Personal Assistant Mode" that supports:
2. Ship a minimal mobile companion shell (iOS + Android) for registration, status, push token, and message handoff.
3. Add signed release artifacts and installation docs.
Status update (2026-02-27): companion bootstrap-manifest export is now available via `flynn companion --export-bootstrap <path|->` as a packaging contract for desktop/mobile shells, `flynn companion --export-release-bundle <dir>` now emits bundle artifacts (bootstrap JSON + launcher + README + `CHECKSUMS.sha256` + `RELEASE_MANIFEST.json`, optional `CHECKSUMS.sha256.sig` with `--signing-key`), `flynn companion --verify-release-bundle <dir>` now validates checksum/signature artifacts before install, `pnpm companion:bundle -- --output <dir> ...` now provides one-pass build-and-verify automation, `.github/workflows/companion-release-bundle.yml` provides CI artifact build/verify/upload, `flynn companion --export-shell-template <dir>` now emits macOS/iOS/Android starter shell templates including iOS/Android runtime skeletons for register/status/location/push/handoff flows, `pnpm companion:reference-apps` now regenerates in-repo macOS/iOS/Android reference app starter directories plus `apps/companion/macos-app` runnable menu-bar scaffold, and `flynn companion` supports one-shot status/location/push bootstrap flags (`--app-version`, `--latitude/--longitude`, `--push-token`) so thin shells can initialize companion metadata in a single run.
Status update (2026-02-27): companion bootstrap-manifest export is now available via `flynn companion --export-bootstrap <path|->` as a packaging contract for desktop/mobile shells, `flynn companion --export-release-bundle <dir>` now emits bundle artifacts (bootstrap JSON + launcher + README + `CHECKSUMS.sha256` + `RELEASE_MANIFEST.json`, optional `CHECKSUMS.sha256.sig` with `--signing-key`), `flynn companion --verify-release-bundle <dir>` now validates checksum/signature artifacts before install, `pnpm companion:bundle -- --output <dir> ...` now provides one-pass build-and-verify automation, `.github/workflows/companion-release-bundle.yml` provides CI artifact build/verify/upload, `flynn companion --export-shell-template <dir>` now emits macOS/iOS/Android starter shell templates including iOS/Android runtime skeletons for register/status/location/push/handoff flows, `pnpm companion:reference-apps` now regenerates in-repo macOS/iOS/Android reference app starter directories plus `apps/companion/macos-app` runnable menu-bar scaffold with a reproducible default `generatedAt` timestamp, and `flynn companion` supports one-shot status/location/push bootstrap flags (`--app-version`, `--latitude/--longitude`, `--push-token`) so thin shells can initialize companion metadata in a single run.
### Implementation Anchors
+1 -1
View File
@@ -7125,7 +7125,7 @@
"status": "completed",
"date": "2026-02-27",
"updated": "2026-02-27",
"summary": "Added repo-shipped companion reference app surfaces for macOS/iOS/Android and a runnable macOS menu-bar Swift Package scaffold (`apps/companion/macos-app`). `generateReferenceCompanionApps()` and `pnpm companion:reference-apps` regenerate these starter directories from typed bootstrap contracts.",
"summary": "Added repo-shipped companion reference app surfaces for macOS/iOS/Android and a runnable macOS menu-bar Swift Package scaffold (`apps/companion/macos-app`). `generateReferenceCompanionApps()` and `pnpm companion:reference-apps` regenerate these starter directories from typed bootstrap contracts with a reproducible default `generatedAt` timestamp (override via `--generated-at`).",
"files_modified": [
"src/companion/referenceApps.ts",
"src/companion/referenceApps.test.ts",
@@ -1,6 +1,8 @@
import { resolve } from 'node:path';
import { generateReferenceCompanionApps } from '../src/companion/index.js';
const DEFAULT_GENERATED_AT = '2026-02-27T00:00:00.000Z';
function getArg(name: string): string | undefined {
const idx = process.argv.indexOf(name);
if (idx < 0) {
@@ -16,10 +18,16 @@ function getArg(name: string): string | undefined {
async function main(): Promise<void> {
const outputDir = resolve(getArg('--output') ?? 'apps/companion');
const gatewayUrl = getArg('--url') ?? 'ws://127.0.0.1:18800';
const generatedAtArg = getArg('--generated-at');
const generatedAt = new Date(generatedAtArg ?? DEFAULT_GENERATED_AT);
if (Number.isNaN(generatedAt.getTime())) {
throw new Error(`Invalid --generated-at value: ${generatedAtArg}`);
}
const result = await generateReferenceCompanionApps({
outputDir,
gatewayUrl,
generatedAt,
});
console.log(`Generated companion reference apps in ${result.rootDir}`);
@@ -28,6 +36,7 @@ async function main(): Promise<void> {
}
console.log(`- macos-app: ${result.macosMenuBarAppDir}`);
console.log(`- readme: ${result.readmePath}`);
console.log(`- generatedAt: ${generatedAt.toISOString()}`);
}
main().catch((error: unknown) => {
+8 -1
View File
@@ -8,18 +8,23 @@ describe('generateReferenceCompanionApps', () => {
it('writes macos/ios/android reference app templates', async () => {
const tempDir = await mkdtemp(join(tmpdir(), 'flynn-reference-apps-'));
const outputDir = join(tempDir, 'apps');
const generatedAt = new Date('2026-02-27T12:34:56.000Z');
const result = await generateReferenceCompanionApps({
outputDir,
gatewayUrl: 'ws://127.0.0.1:18800',
generatedAt: new Date('2026-02-27T00:00:00.000Z'),
generatedAt,
});
expect(result.generated.map((entry) => entry.platform)).toEqual(['macos', 'ios', 'android']);
const macosTemplate = await readFile(`${outputDir}/macos/MenuBarCompanion.swift`, 'utf8');
const macosAppMain = await readFile(`${outputDir}/macos-app/Sources/FlynnCompanionMenuBar/main.swift`, 'utf8');
const iosBootstrapRaw = await readFile(`${outputDir}/ios/companion.bootstrap.json`, 'utf8');
const macosAppBootstrapRaw = await readFile(`${outputDir}/macos-app/Sources/FlynnCompanionMenuBar/Resources/companion.bootstrap.json`, 'utf8');
const iosTemplate = await readFile(`${outputDir}/ios/CompanionBootstrap.swift`, 'utf8');
const androidTemplate = await readFile(`${outputDir}/android/CompanionBootstrap.kt`, 'utf8');
const rootReadme = await readFile(`${outputDir}/README.md`, 'utf8');
const iosBootstrap = JSON.parse(iosBootstrapRaw) as { generatedAt: string };
const macosAppBootstrap = JSON.parse(macosAppBootstrapRaw) as { generatedAt: string };
expect(macosTemplate).toContain('launchFlynnCompanion');
expect(macosAppMain).toContain('NSStatusBar.system.statusItem');
@@ -28,6 +33,8 @@ describe('generateReferenceCompanionApps', () => {
expect(rootReadme).toContain('Companion Reference Apps');
expect(result.macosMenuBarAppDir).toBe(`${outputDir}/macos-app`);
expect(result.macosMenuBarAppFiles.length).toBeGreaterThan(0);
expect(iosBootstrap.generatedAt).toBe(generatedAt.toISOString());
expect(macosAppBootstrap.generatedAt).toBe(generatedAt.toISOString());
await rm(tempDir, { recursive: true, force: true });
});