diff --git a/README.md b/README.md index fd303a3..138a920 100644 --- a/README.md +++ b/README.md @@ -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 `). - 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`. diff --git a/apps/companion/android/companion.bootstrap.json b/apps/companion/android/companion.bootstrap.json index 60a9a28..d7e76fa 100644 --- a/apps/companion/android/companion.bootstrap.json +++ b/apps/companion/android/companion.bootstrap.json @@ -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" }, diff --git a/apps/companion/ios/companion.bootstrap.json b/apps/companion/ios/companion.bootstrap.json index 429f009..3eb8d86 100644 --- a/apps/companion/ios/companion.bootstrap.json +++ b/apps/companion/ios/companion.bootstrap.json @@ -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" }, diff --git a/apps/companion/macos-app/Sources/FlynnCompanionMenuBar/Resources/companion.bootstrap.json b/apps/companion/macos-app/Sources/FlynnCompanionMenuBar/Resources/companion.bootstrap.json index f99b1f6..20301b4 100644 --- a/apps/companion/macos-app/Sources/FlynnCompanionMenuBar/Resources/companion.bootstrap.json +++ b/apps/companion/macos-app/Sources/FlynnCompanionMenuBar/Resources/companion.bootstrap.json @@ -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" }, diff --git a/apps/companion/macos/companion.bootstrap.json b/apps/companion/macos/companion.bootstrap.json index 49ce5ee..c1e4d8f 100644 --- a/apps/companion/macos/companion.bootstrap.json +++ b/apps/companion/macos/companion.bootstrap.json @@ -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" }, diff --git a/docs/operations/COMPANION_RELEASE_BUNDLE.md b/docs/operations/COMPANION_RELEASE_BUNDLE.md index 2b20a32..93e62da 100644 --- a/docs/operations/COMPANION_RELEASE_BUNDLE.md +++ b/docs/operations/COMPANION_RELEASE_BUNDLE.md @@ -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 ` if you need a different value. CI automation: diff --git a/docs/plans/2026-02-26-personal-assistant-productization-plan.md b/docs/plans/2026-02-26-personal-assistant-productization-plan.md index 8bfc8a7..5afa925 100644 --- a/docs/plans/2026-02-26-personal-assistant-productization-plan.md +++ b/docs/plans/2026-02-26-personal-assistant-productization-plan.md @@ -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 ` as a packaging contract for desktop/mobile shells, `flynn companion --export-release-bundle ` 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 ` now validates checksum/signature artifacts before install, `pnpm companion:bundle -- --output ...` 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 ` 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 ` as a packaging contract for desktop/mobile shells, `flynn companion --export-release-bundle ` 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 ` now validates checksum/signature artifacts before install, `pnpm companion:bundle -- --output ...` 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 ` 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 diff --git a/docs/plans/state.json b/docs/plans/state.json index 2f1152b..bfa9233 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -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", diff --git a/scripts/export-companion-reference-apps.ts b/scripts/export-companion-reference-apps.ts index 21da3b6..fd89d48 100644 --- a/scripts/export-companion-reference-apps.ts +++ b/scripts/export-companion-reference-apps.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 { 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 { } console.log(`- macos-app: ${result.macosMenuBarAppDir}`); console.log(`- readme: ${result.readmePath}`); + console.log(`- generatedAt: ${generatedAt.toISOString()}`); } main().catch((error: unknown) => { diff --git a/src/companion/referenceApps.test.ts b/src/companion/referenceApps.test.ts index 9e587d7..0dca59d 100644 --- a/src/companion/referenceApps.test.ts +++ b/src/companion/referenceApps.test.ts @@ -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 }); });