From be8b1f29a4dd2c9f53f2c51d646c115fb3e4c961 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Thu, 26 Feb 2026 19:37:28 -0800 Subject: [PATCH] feat(companion): add generated macos ios android reference app surfaces --- README.md | 2 + apps/companion/README.md | 9 ++ apps/companion/android/CompanionBootstrap.kt | 32 +++++++ apps/companion/android/README.md | 11 +++ .../android/companion.bootstrap.json | 23 +++++ apps/companion/ios/CompanionBootstrap.swift | 32 +++++++ apps/companion/ios/README.md | 11 +++ apps/companion/ios/companion.bootstrap.json | 23 +++++ apps/companion/macos/MenuBarCompanion.swift | 38 ++++++++ apps/companion/macos/README.md | 11 +++ apps/companion/macos/companion.bootstrap.json | 23 +++++ docs/api/PROTOCOL.md | 1 + docs/architecture/AGENT_DIAGRAM.md | 1 + .../GATEWAY_SESSIONS_AND_QUEUE.md | 1 + docs/operations/COMPANION_RELEASE_BUNDLE.md | 6 ++ ...-personal-assistant-productization-plan.md | 2 +- docs/plans/state.json | 37 +++++++- package.json | 3 +- scripts/export-companion-reference-apps.ts | 36 ++++++++ src/companion/index.ts | 6 ++ src/companion/referenceApps.test.ts | 30 +++++++ src/companion/referenceApps.ts | 86 +++++++++++++++++++ 22 files changed, 419 insertions(+), 5 deletions(-) create mode 100644 apps/companion/README.md create mode 100644 apps/companion/android/CompanionBootstrap.kt create mode 100644 apps/companion/android/README.md create mode 100644 apps/companion/android/companion.bootstrap.json create mode 100644 apps/companion/ios/CompanionBootstrap.swift create mode 100644 apps/companion/ios/README.md create mode 100644 apps/companion/ios/companion.bootstrap.json create mode 100644 apps/companion/macos/MenuBarCompanion.swift create mode 100644 apps/companion/macos/README.md create mode 100644 apps/companion/macos/companion.bootstrap.json create mode 100644 scripts/export-companion-reference-apps.ts create mode 100644 src/companion/referenceApps.test.ts create mode 100644 src/companion/referenceApps.ts diff --git a/README.md b/README.md index eabe6eb..89b8a5f 100644 --- a/README.md +++ b/README.md @@ -1739,6 +1739,7 @@ Companion runtime helper: - `src/companion/shellTemplate.ts` provides `writeCompanionShellTemplate()` for emitting platform starter shells (macOS/iOS/Android native scaffold snippets + bootstrap JSON). - `src/companion/releaseVerify.ts` provides `verifyCompanionReleaseBundle()` for validating bundle checksums and optional signature metadata. - `src/companion/releasePipeline.ts` provides `buildAndVerifyCompanionReleaseBundle()` for build-and-verify automation (including signed bundle flows). +- `src/companion/referenceApps.ts` provides `generateReferenceCompanionApps()` for deterministic macOS/iOS/Android reference app starter exports. Minimal companion CLI: - `flynn companion --once` connects to the gateway, registers a node, publishes one heartbeat, then exits. @@ -1750,6 +1751,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`, native starter file, `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 directories. `run-companion.sh` verifies bundle checksums (`CHECKSUMS.sha256`) before launching `flynn companion`. - `flynn companion --once --platform ios --app-version 1.2.3 --device-name "iPhone" --status-text ready --battery-pct 84 --power-source battery` sends richer initial node status metadata. diff --git a/apps/companion/README.md b/apps/companion/README.md new file mode 100644 index 0000000..d220640 --- /dev/null +++ b/apps/companion/README.md @@ -0,0 +1,9 @@ +# Companion Reference Apps + +This directory contains generated companion starter shells for: + +- macOS menu-bar style wrapper +- iOS shell +- Android shell + +These are reference starters, not production binaries. Use them as a baseline for app packaging and distribution workflows. diff --git a/apps/companion/android/CompanionBootstrap.kt b/apps/companion/android/CompanionBootstrap.kt new file mode 100644 index 0000000..5af6914 --- /dev/null +++ b/apps/companion/android/CompanionBootstrap.kt @@ -0,0 +1,32 @@ +package flynn.companion + +// Reference Android bootstrap model for integrating with Flynn gateway runtime. +// Wire FCM token refresh to node.push_token.set and app lifecycle to heartbeat publishing. + +data class CompanionBootstrap( + val schemaVersion: Int, + val generatedAt: String, + val gateway: Gateway, + val node: Node, + val runtime: Runtime +) + +data class Gateway( + val url: String, + val token: String? +) + +data class Node( + val nodeId: String, + val role: String, + val platform: String, + val capabilities: List +) + +data class Runtime( + val heartbeatSeconds: Int, + val handoffTimeoutMs: Int, + val autoReconnect: Boolean +) + +// Generated for node: android-reference-shell (android) diff --git a/apps/companion/android/README.md b/apps/companion/android/README.md new file mode 100644 index 0000000..355fb27 --- /dev/null +++ b/apps/companion/android/README.md @@ -0,0 +1,11 @@ +# Flynn Companion android Shell Template + +This directory contains a generated starter template for a android companion shell. + +Files: +- `companion.bootstrap.json`: resolved Flynn companion bootstrap contract +- `CompanionBootstrap.kt`: platform-native starter model/wrapper snippet + +Notes: +- These templates are intentionally minimal and should be integrated into your app project. +- Runtime transport should use Flynn gateway JSON-RPC node methods (`node.register`, `node.status.set`, `node.location.set`, `node.push_token.set`). diff --git a/apps/companion/android/companion.bootstrap.json b/apps/companion/android/companion.bootstrap.json new file mode 100644 index 0000000..3ffca61 --- /dev/null +++ b/apps/companion/android/companion.bootstrap.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "generatedAt": "2026-02-27T03:35:54.245Z", + "gateway": { + "url": "ws://127.0.0.1:18800" + }, + "node": { + "nodeId": "android-reference-shell", + "role": "companion", + "platform": "android", + "capabilities": [ + "ui.canvas", + "node.status.write", + "node.location.write", + "node.push.register" + ] + }, + "runtime": { + "heartbeatSeconds": 30, + "handoffTimeoutMs": 120000, + "autoReconnect": true + } +} diff --git a/apps/companion/ios/CompanionBootstrap.swift b/apps/companion/ios/CompanionBootstrap.swift new file mode 100644 index 0000000..ad11fb9 --- /dev/null +++ b/apps/companion/ios/CompanionBootstrap.swift @@ -0,0 +1,32 @@ +import Foundation + +// Reference iOS bootstrap model for integrating with Flynn gateway runtime. +// Wire APNs token refresh to node.push_token.set and app lifecycle to heartbeat publishing. + +struct CompanionBootstrap: Codable { + let schemaVersion: Int + let generatedAt: String + let gateway: Gateway + let node: Node + let runtime: Runtime +} + +struct Gateway: Codable { + let url: String + let token: String? +} + +struct Node: Codable { + let nodeId: String + let role: String + let platform: String + let capabilities: [String] +} + +struct Runtime: Codable { + let heartbeatSeconds: Int + let handoffTimeoutMs: Int + let autoReconnect: Bool +} + +// Generated for node: ios-reference-shell (ios) diff --git a/apps/companion/ios/README.md b/apps/companion/ios/README.md new file mode 100644 index 0000000..1e83261 --- /dev/null +++ b/apps/companion/ios/README.md @@ -0,0 +1,11 @@ +# Flynn Companion ios Shell Template + +This directory contains a generated starter template for a ios companion shell. + +Files: +- `companion.bootstrap.json`: resolved Flynn companion bootstrap contract +- `CompanionBootstrap.swift`: platform-native starter model/wrapper snippet + +Notes: +- These templates are intentionally minimal and should be integrated into your app project. +- Runtime transport should use Flynn gateway JSON-RPC node methods (`node.register`, `node.status.set`, `node.location.set`, `node.push_token.set`). diff --git a/apps/companion/ios/companion.bootstrap.json b/apps/companion/ios/companion.bootstrap.json new file mode 100644 index 0000000..f4dee50 --- /dev/null +++ b/apps/companion/ios/companion.bootstrap.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "generatedAt": "2026-02-27T03:35:54.245Z", + "gateway": { + "url": "ws://127.0.0.1:18800" + }, + "node": { + "nodeId": "ios-reference-shell", + "role": "companion", + "platform": "ios", + "capabilities": [ + "ui.canvas", + "node.status.write", + "node.location.write", + "node.push.register" + ] + }, + "runtime": { + "heartbeatSeconds": 30, + "handoffTimeoutMs": 120000, + "autoReconnect": true + } +} diff --git a/apps/companion/macos/MenuBarCompanion.swift b/apps/companion/macos/MenuBarCompanion.swift new file mode 100644 index 0000000..17309d3 --- /dev/null +++ b/apps/companion/macos/MenuBarCompanion.swift @@ -0,0 +1,38 @@ +import Foundation + +struct CompanionBootstrap: Codable { + let schemaVersion: Int + let generatedAt: String + let gateway: Gateway + let node: Node + let runtime: Runtime +} + +struct Gateway: Codable { + let url: String + let token: String? +} + +struct Node: Codable { + let nodeId: String + let role: String + let platform: String + let capabilities: [String] +} + +struct Runtime: Codable { + let heartbeatSeconds: Int + let handoffTimeoutMs: Int + let autoReconnect: Bool +} + +// Reference entrypoint for a menu-bar app wrapper. +// Production apps should prefer in-process runtime integration over shelling out. +func launchFlynnCompanion() throws { + let task = Process() + task.executableURL = URL(fileURLWithPath: "/bin/bash") + task.arguments = ["./run-companion.sh"] + try task.run() +} + +// Generated for node: macos-reference-shell (macos) diff --git a/apps/companion/macos/README.md b/apps/companion/macos/README.md new file mode 100644 index 0000000..bccde38 --- /dev/null +++ b/apps/companion/macos/README.md @@ -0,0 +1,11 @@ +# Flynn Companion macos Shell Template + +This directory contains a generated starter template for a macos companion shell. + +Files: +- `companion.bootstrap.json`: resolved Flynn companion bootstrap contract +- `MenuBarCompanion.swift`: platform-native starter model/wrapper snippet + +Notes: +- These templates are intentionally minimal and should be integrated into your app project. +- Runtime transport should use Flynn gateway JSON-RPC node methods (`node.register`, `node.status.set`, `node.location.set`, `node.push_token.set`). diff --git a/apps/companion/macos/companion.bootstrap.json b/apps/companion/macos/companion.bootstrap.json new file mode 100644 index 0000000..2f6f86d --- /dev/null +++ b/apps/companion/macos/companion.bootstrap.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "generatedAt": "2026-02-27T03:35:54.245Z", + "gateway": { + "url": "ws://127.0.0.1:18800" + }, + "node": { + "nodeId": "macos-reference-shell", + "role": "companion", + "platform": "macos", + "capabilities": [ + "ui.canvas", + "node.status.write", + "node.location.write", + "node.push.register" + ] + }, + "runtime": { + "heartbeatSeconds": 30, + "handoffTimeoutMs": 120000, + "autoReconnect": true + } +} diff --git a/docs/api/PROTOCOL.md b/docs/api/PROTOCOL.md index 38b237a..dfb51dd 100644 --- a/docs/api/PROTOCOL.md +++ b/docs/api/PROTOCOL.md @@ -1867,3 +1867,4 @@ For more implementation details, see: - Companion release bundle verifier: `src/companion/releaseVerify.ts` (validates `CHECKSUMS.sha256` and optional signature metadata against a provided public key) - Companion release automation pipeline: `src/companion/releasePipeline.ts` + `scripts/build-companion-release-bundle.ts` (build-and-verify workflow for deterministic companion artifact generation) - Companion shell template helper: `src/companion/shellTemplate.ts` (writes platform-native starter template files for `macos`, `ios`, and `android` shell scaffolding) +- Companion reference app exporter: `src/companion/referenceApps.ts` + `scripts/export-companion-reference-apps.ts` (regenerates in-repo platform starter app directories) diff --git a/docs/architecture/AGENT_DIAGRAM.md b/docs/architecture/AGENT_DIAGRAM.md index fc956d7..29dabe0 100644 --- a/docs/architecture/AGENT_DIAGRAM.md +++ b/docs/architecture/AGENT_DIAGRAM.md @@ -161,6 +161,7 @@ Gateway streaming UX signals: - `flynn companion --verify-release-bundle ` can validate bundle checksums and optional signatures before installation or rollout. - `flynn companion --export-shell-template ` can emit platform starter shell templates (macOS/iOS/Android native scaffold files + bootstrap JSON) for reference app bootstrapping. - `pnpm companion:bundle -- --output ...` runs a build-and-verify release pipeline for repeatable companion artifact generation. +- `pnpm companion:reference-apps -- --output apps/companion` regenerates in-repo macOS/iOS/Android reference app starter surfaces. - `flynn companion` can bootstrap status/location/push metadata on connect (`node.status.set` + optional `node.location.set`/`node.push_token.set`) so thin companion shells can register operational context in one launch. - Canvas artifacts are persisted by the gateway so session UI surfaces can recover after daemon restarts. - TTS synthesis uses an ordered provider chain with health cooldown tracking; if all providers fail, replies degrade to text-only without dropping the response. diff --git a/docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md b/docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md index dfefaa2..d80049b 100644 --- a/docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md +++ b/docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md @@ -27,6 +27,7 @@ If you only want the protocol surface, see `docs/api/PROTOCOL.md`. - Companion release bundles can be verified before install via `flynn companion --verify-release-bundle ` with optional signature-key checks. - Companion packaging automation is available via `pnpm companion:bundle -- --output ...`, which builds and verifies the release bundle in one pass. - Companion platform starter scaffolds can be generated via `flynn companion --export-shell-template ` for macOS/iOS/Android reference app bootstrapping. +- Companion reference app directories can be regenerated via `pnpm companion:reference-apps -- --output apps/companion` for repo-shipped starter surfaces. - Companion CLI supports one-shot shell bootstrap metadata for live sessions (`--app-version`/`--status-text`, `--latitude`/`--longitude`, `--push-token`) so desktop/mobile wrappers can initialize node status/location/push in a single launch flow. - Canvas artifacts are persisted per session under the gateway data directory for UI recovery across restarts. - TTS output is best-effort with ordered provider fallback + per-provider cooldown tracking; synthesis failures still fall back to text-only responses. diff --git a/docs/operations/COMPANION_RELEASE_BUNDLE.md b/docs/operations/COMPANION_RELEASE_BUNDLE.md index f2159aa..870c170 100644 --- a/docs/operations/COMPANION_RELEASE_BUNDLE.md +++ b/docs/operations/COMPANION_RELEASE_BUNDLE.md @@ -48,6 +48,12 @@ pnpm companion:bundle -- \ This script builds the bundle and immediately verifies checksums/signatures before returning success. +Reference app starters can be regenerated in-repo with: + +```bash +pnpm companion:reference-apps -- --output ./apps/companion +``` + ## Generate Platform Starter Shell Template For native app bootstrapping (without launcher/checksum artifacts), export a platform template: 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 d34dc24..be68e6e 100644 --- a/docs/plans/2026-02-26-personal-assistant-productization-plan.md +++ b/docs/plans/2026-02-26-personal-assistant-productization-plan.md @@ -49,7 +49,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`, 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, `flynn companion --export-shell-template ` now emits macOS/iOS/Android starter shell templates, 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`, 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, `flynn companion --export-shell-template ` now emits macOS/iOS/Android starter shell templates, `pnpm companion:reference-apps` now regenerates in-repo macOS/iOS/Android reference app starter directories, 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 4345145..124eeeb 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -7112,10 +7112,41 @@ "docs/plans/state.json" ], "test_status": "pnpm test:run src/companion/releasePipeline.test.ts src/companion/releaseBundle.test.ts src/companion/releaseVerify.test.ts + pnpm typecheck passing" + }, + "personal-assistant-productization-phase1-companion-reference-app-surfaces": { + "status": "completed", + "date": "2026-02-27", + "updated": "2026-02-27", + "summary": "Added repo-shipped companion reference app surfaces for macOS/iOS/Android. New `generateReferenceCompanionApps()` helper and `pnpm companion:reference-apps` script deterministically regenerate `apps/companion/*` starter directories from typed bootstrap contracts.", + "files_modified": [ + "src/companion/referenceApps.ts", + "src/companion/referenceApps.test.ts", + "src/companion/index.ts", + "scripts/export-companion-reference-apps.ts", + "package.json", + "apps/companion/README.md", + "apps/companion/macos/MenuBarCompanion.swift", + "apps/companion/macos/README.md", + "apps/companion/macos/companion.bootstrap.json", + "apps/companion/ios/CompanionBootstrap.swift", + "apps/companion/ios/README.md", + "apps/companion/ios/companion.bootstrap.json", + "apps/companion/android/CompanionBootstrap.kt", + "apps/companion/android/README.md", + "apps/companion/android/companion.bootstrap.json", + "README.md", + "docs/api/PROTOCOL.md", + "docs/architecture/AGENT_DIAGRAM.md", + "docs/architecture/GATEWAY_SESSIONS_AND_QUEUE.md", + "docs/operations/COMPANION_RELEASE_BUNDLE.md", + "docs/plans/2026-02-26-personal-assistant-productization-plan.md", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/companion/referenceApps.test.ts src/companion/shellTemplate.test.ts + pnpm typecheck passing" } }, "overall_progress": { - "total_test_count": 2584, + "total_test_count": 2585, "all_tests_passing": true, "p0_completion": "3/3 (100%)", "p1_completion": "4/4 (100%)", @@ -7130,7 +7161,7 @@ "tier2_completion": "4/4 (100%) — inbound webhooks, vector memory search, Dockerfile, heartbeat monitor", "tier3_completion": "5/5 (100%) — lane queue, credential redaction, web UI token dashboard, xAI (Grok) provider, Voyage AI embeddings", "tier4_completion": "4/4 (100%) — gateway lock, shell completion, Tailscale Serve/Funnel, DM pairing codes", - "feature_gap_scorecard": "rebaselined 2026-02-26 and updated 2026-02-27 (phase 3 + phase 1 + phase 2 + phase 4 slices + companion packaging/bundle tooling + shell bootstrap controls + platform templates + signed/verified artifacts + automation pipeline) — channel breadth, setup wizard, baseline browser automation, subagent controls, browser workflow reliability primitives (wait/assert/extract/retries/checkpoints/guardrails/budgets), companion reconnect/runtime-handoff foundations, companion packaging primitives (bootstrap export + release-bundle artifacts + checksum manifests + optional signatures + verification mode + checksum-gated launcher + one-pass build/verify automation), companion install/verification runbook, platform starter shell templates for macOS/iOS/Android, and one-shot status/location/push shell bootstrap controls, voice reliability hardening (talk controls + TTS fallback/health + interruption-safe cancel semantics), and onboarding first-success funnel improvements are implemented; remaining high-impact personal-assistant gaps center on production-grade companion app binaries and end-to-end distribution workflows.", + "feature_gap_scorecard": "rebaselined 2026-02-26 and updated 2026-02-27 (phase 3 + phase 1 + phase 2 + phase 4 slices + companion packaging/bundle tooling + shell bootstrap controls + platform templates + signed/verified artifacts + automation pipeline + reference app surfaces) — channel breadth, setup wizard, baseline browser automation, subagent controls, browser workflow reliability primitives (wait/assert/extract/retries/checkpoints/guardrails/budgets), companion reconnect/runtime-handoff foundations, companion packaging primitives (bootstrap export + release-bundle artifacts + checksum manifests + optional signatures + verification mode + checksum-gated launcher + one-pass build/verify automation), companion install/verification runbook, repo-shipped macOS/iOS/Android reference app starter surfaces, and one-shot status/location/push shell bootstrap controls, voice reliability hardening (talk controls + TTS fallback/health + interruption-safe cancel semantics), and onboarding first-success funnel improvements are implemented; remaining high-impact personal-assistant gaps center on production-grade native companion binaries and distribution hardening automation.", "operator_dx_milestone": "Phase 3 (Live Ops Dashboard): 2/2 plans complete — milestone done", "dashboard_observability": "completed — service health graphs + core service log viewer added to web UI via observability RPCs and bounded backend sampling", "gmail_auth_cli": "flynn gmail-auth command implemented with OAuth2 flow, doctor check, config routed to Telegram", @@ -7163,7 +7194,7 @@ "deeper_surfaces_phase3_companion_canvas_voice": "completed — companion reconnect resilience (auto-reconnect with backoff, pending-wait cancellation on disconnect), canvas artifact persistence (SQLite-backed store, daemon-restart durability), voice TTS fallback coverage (text-only reply on TTS failure, no dropped responses)", "deeper_surfaces_phase4_rollout": "completed — phase 4 rollout and operator readiness plan documented: canary rollout plan by feature flag/surface, explicit rollback playbook, operator docs and architecture/protocol docs synchronized", "post_phase_test_fixes": "completed — fixed 4 test failures introduced by phases 1-3: iOS/Android push listNodes (missing publishHeartbeat before platform-filtered query), server.test agent.send (run_state events now precede done; added sendAndWaitForDone helper), httpBody 413 (req.destroy() closed socket before response could be sent; replaced with Connection: close header on 413 responses)", - "personal_assistant_productization_plan": "in_progress — 8-10 week phased roadmap active; Phase 3 browser workflow reliability shipped, Phase 1 companion runtime reliability includes reconnect state replay + typed handoff support, companion packaging primitives now include bootstrap manifest export, release-bundle artifact generation, checksum manifests, optional signature emission, verification mode, checksum-gated launchers, one-pass build/verify automation pipeline, platform starter shell-template generation, and an install/verification runbook, companion shell bootstrap controls cover status/location/push metadata, Phase 2 voice reliability ships talk controls + TTS provider fallback/health + interruption-safe voice cancel mapping, and Phase 4 onboarding includes Personal Assistant Mode preset + live readiness checks + first-success guidance. Remaining phase focus: production-ready companion app surfaces and end-to-end distribution workflows.", + "personal_assistant_productization_plan": "in_progress — 8-10 week phased roadmap active; Phase 3 browser workflow reliability shipped, Phase 1 companion runtime reliability includes reconnect state replay + typed handoff support, companion packaging primitives include bootstrap manifest export, release-bundle artifact generation, checksum manifests, optional signature emission, verification mode, checksum-gated launchers, one-pass build/verify automation pipeline, platform starter shell-template generation, repo-shipped reference app starter directories, and an install/verification runbook, companion shell bootstrap controls cover status/location/push metadata, Phase 2 voice reliability ships talk controls + TTS provider fallback/health + interruption-safe voice cancel mapping, and Phase 4 onboarding includes Personal Assistant Mode preset + live readiness checks + first-success guidance. Remaining phase focus: production-ready native companion app surfaces and distribution hardening automation.", "subagents_support": "completed — subagent phases 1-3 shipped with `subagent.spawn/send/list/cancel/delete/summary`, per-child queue mode (`followup|interrupt`), budgets (`max_turns`, `max_total_tokens`, `turn_timeout_ms`), tool-profile overrides, trace-linked audit events, `/subagents` inspection commands, and focused regression tests." }, "soul_md_and_cron_create": { diff --git a/package.json b/package.json index 7b9d172..1d070c9 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "audit:backend-canary": "node --import tsx/esm scripts/summarize-backend-canary.ts", "audit:phase0-baseline": "node --import tsx/esm scripts/summarize-phase0-baseline.ts", "audit:backend-canary:probes": "node --import tsx/esm scripts/run-pi-canary-guard-probes.ts", - "companion:bundle": "node --import tsx/esm scripts/build-companion-release-bundle.ts" + "companion:bundle": "node --import tsx/esm scripts/build-companion-release-bundle.ts", + "companion:reference-apps": "node --import tsx/esm scripts/export-companion-reference-apps.ts" }, "keywords": [ "ai", diff --git a/scripts/export-companion-reference-apps.ts b/scripts/export-companion-reference-apps.ts new file mode 100644 index 0000000..9934b28 --- /dev/null +++ b/scripts/export-companion-reference-apps.ts @@ -0,0 +1,36 @@ +import { resolve } from 'node:path'; +import { generateReferenceCompanionApps } from '../src/companion/index.js'; + +function getArg(name: string): string | undefined { + const idx = process.argv.indexOf(name); + if (idx < 0) { + return undefined; + } + const value = process.argv[idx + 1]; + if (!value || value.startsWith('--')) { + return undefined; + } + return value; +} + +async function main(): Promise { + const outputDir = resolve(getArg('--output') ?? 'apps/companion'); + const gatewayUrl = getArg('--url') ?? 'ws://127.0.0.1:18800'; + + const result = await generateReferenceCompanionApps({ + outputDir, + gatewayUrl, + }); + + console.log(`Generated companion reference apps in ${result.rootDir}`); + for (const entry of result.generated) { + console.log(`- ${entry.platform}: ${entry.outputDir}`); + } + console.log(`- readme: ${result.readmePath}`); +} + +main().catch((error: unknown) => { + const message = error instanceof Error ? error.message : String(error); + console.error(`companion:reference-apps failed: ${message}`); + process.exitCode = 1; +}); diff --git a/src/companion/index.ts b/src/companion/index.ts index 749f7c3..50740a9 100644 --- a/src/companion/index.ts +++ b/src/companion/index.ts @@ -14,6 +14,7 @@ export { writeCompanionReleaseBundle } from './releaseBundle.js'; export { writeCompanionShellTemplate } from './shellTemplate.js'; export { verifyCompanionReleaseBundle } from './releaseVerify.js'; export { buildAndVerifyCompanionReleaseBundle } from './releasePipeline.js'; +export { generateReferenceCompanionApps } from './referenceApps.js'; export type { CompanionRuntimeClientOptions, @@ -98,3 +99,8 @@ export type { BuildAndVerifyCompanionReleaseBundleInput, BuildAndVerifyCompanionReleaseBundleResult, } from './releasePipeline.js'; +export type { + GenerateReferenceCompanionAppsInput, + GenerateReferenceCompanionAppsResult, + GeneratedReferenceCompanionApp, +} from './referenceApps.js'; diff --git a/src/companion/referenceApps.test.ts b/src/companion/referenceApps.test.ts new file mode 100644 index 0000000..73e8cbf --- /dev/null +++ b/src/companion/referenceApps.test.ts @@ -0,0 +1,30 @@ +import { mkdtemp, readFile, rm } from 'node:fs/promises'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { describe, expect, it } from 'vitest'; +import { generateReferenceCompanionApps } from './referenceApps.js'; + +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 result = await generateReferenceCompanionApps({ + outputDir, + gatewayUrl: 'ws://127.0.0.1:18800', + generatedAt: new Date('2026-02-27T00:00:00.000Z'), + }); + + expect(result.generated.map((entry) => entry.platform)).toEqual(['macos', 'ios', 'android']); + const macosTemplate = await readFile(`${outputDir}/macos/MenuBarCompanion.swift`, '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'); + + expect(macosTemplate).toContain('launchFlynnCompanion'); + expect(iosTemplate).toContain('CompanionBootstrap'); + expect(androidTemplate).toContain('data class CompanionBootstrap'); + expect(rootReadme).toContain('Companion Reference Apps'); + + await rm(tempDir, { recursive: true, force: true }); + }); +}); diff --git a/src/companion/referenceApps.ts b/src/companion/referenceApps.ts new file mode 100644 index 0000000..63ce94d --- /dev/null +++ b/src/companion/referenceApps.ts @@ -0,0 +1,86 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { writeCompanionShellTemplate } from './shellTemplate.js'; +import type { CompanionBootstrapPlatform } from './bootstrapManifest.js'; + +export interface GenerateReferenceCompanionAppsInput { + outputDir: string; + gatewayUrl: string; + generatedAt?: Date; +} + +export interface GeneratedReferenceCompanionApp { + platform: 'macos' | 'ios' | 'android'; + outputDir: string; + files: string[]; +} + +export interface GenerateReferenceCompanionAppsResult { + rootDir: string; + generated: GeneratedReferenceCompanionApp[]; + readmePath: string; +} + +function defaultCapabilities(platform: CompanionBootstrapPlatform): string[] { + if (platform === 'ios' || platform === 'macos' || platform === 'android') { + return ['ui.canvas', 'node.status.write', 'node.location.write', 'node.push.register']; + } + return ['ui.canvas', 'node.status.write']; +} + +function rootReadmeBody(): string { + return `# Companion Reference Apps + +This directory contains generated companion starter shells for: + +- macOS menu-bar style wrapper +- iOS shell +- Android shell + +These are reference starters, not production binaries. Use them as a baseline for app packaging and distribution workflows. +`; +} + +export async function generateReferenceCompanionApps( + input: GenerateReferenceCompanionAppsInput, +): Promise { + await mkdir(input.outputDir, { recursive: true }); + const generatedAt = input.generatedAt ?? new Date(); + const generated: GeneratedReferenceCompanionApp[] = []; + for (const platform of ['macos', 'ios', 'android'] as const) { + const outDir = `${input.outputDir}/${platform}`; + const result = await writeCompanionShellTemplate({ + outputDir: outDir, + platform, + manifest: { + schemaVersion: 1, + generatedAt: generatedAt.toISOString(), + gateway: { + url: input.gatewayUrl, + }, + node: { + nodeId: `${platform}-reference-shell`, + role: 'companion', + platform, + capabilities: defaultCapabilities(platform), + }, + runtime: { + heartbeatSeconds: 30, + handoffTimeoutMs: 120000, + autoReconnect: true, + }, + }, + }); + generated.push({ + platform, + outputDir: result.outputDir, + files: result.files, + }); + } + const readmePath = `${input.outputDir}/README.md`; + await writeFile(readmePath, rootReadmeBody(), 'utf8'); + return { + rootDir: input.outputDir, + generated, + readmePath, + }; +}