multiplat-factory 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -274
- package/dist/chunk-A5ZTNRTN.js +65 -0
- package/dist/chunk-A5ZTNRTN.js.map +1 -0
- package/dist/dashboard/200.html +1 -0
- package/dist/dashboard/404.html +1 -0
- package/dist/dashboard/_nuxt/2mTACkJa.js +3 -0
- package/dist/dashboard/_nuxt/B1MQUUmA.js +1 -0
- package/dist/dashboard/_nuxt/BD1XFGRw.js +1 -0
- package/dist/dashboard/_nuxt/BPFatz28.js +1 -0
- package/dist/dashboard/_nuxt/BSEomhaZ.js +3 -0
- package/dist/dashboard/_nuxt/BVtebpBS.js +1 -0
- package/dist/dashboard/_nuxt/BeCRcAqm.js +1 -0
- package/dist/dashboard/_nuxt/Bk10jxDt.js +1 -0
- package/dist/dashboard/_nuxt/Bn8sQoBR.js +1 -0
- package/dist/dashboard/_nuxt/C0GreNxE.js +1 -0
- package/dist/dashboard/_nuxt/C6vzBuMd.js +1 -0
- package/dist/dashboard/_nuxt/CCMCJdY2.js +1 -0
- package/dist/dashboard/_nuxt/CFi3g9tC.js +1 -0
- package/dist/dashboard/_nuxt/CNarvKcN.js +2 -0
- package/dist/dashboard/_nuxt/CYHUjCqZ.js +3 -0
- package/dist/dashboard/_nuxt/CccylW6m.js +1 -0
- package/dist/dashboard/_nuxt/ClOgtTzr.js +1 -0
- package/dist/dashboard/_nuxt/Cp6G1JDJ.js +1 -0
- package/dist/dashboard/_nuxt/CqvIBbxm.js +1 -0
- package/dist/dashboard/_nuxt/CxmPv0Xg.js +1 -0
- package/dist/dashboard/_nuxt/D2giJ0zb.js +1 -0
- package/dist/dashboard/_nuxt/DD-d0mIO.js +1 -0
- package/dist/dashboard/_nuxt/DSm1cKIU.js +1 -0
- package/dist/dashboard/_nuxt/DYvEcPgU.js +1 -0
- package/dist/dashboard/_nuxt/DkcIzcLy.js +57 -0
- package/dist/dashboard/_nuxt/DlF4qIFG.js +1 -0
- package/dist/dashboard/_nuxt/DoctorView.BWn3KKZd.css +1 -0
- package/dist/dashboard/_nuxt/DxWip8Ok.js +1 -0
- package/dist/dashboard/_nuxt/G-IHUB_t.js +1 -0
- package/dist/dashboard/_nuxt/RgvLL8ZL.js +1 -0
- package/dist/dashboard/_nuxt/ZdpP1Ud-.js +1 -0
- package/dist/dashboard/_nuxt/_id_.BL_xkUo3.css +1 -0
- package/dist/dashboard/_nuxt/builds/latest.json +1 -0
- package/dist/dashboard/_nuxt/builds/meta/1d285342-32d1-4f36-aa29-aea4bf9510b7.json +1 -0
- package/dist/dashboard/_nuxt/default.BwIDAPER.css +1 -0
- package/dist/dashboard/_nuxt/dnzJofwx.js +1 -0
- package/dist/dashboard/_nuxt/entry.DRmJCmhL.css +1 -0
- package/dist/dashboard/_nuxt/error-404.Bq-TOqNj.css +1 -0
- package/dist/dashboard/_nuxt/error-500.xaLU7Cq-.css +1 -0
- package/dist/dashboard/_nuxt/ex3ztmkP.js +1 -0
- package/dist/dashboard/_nuxt/new.BbydyAru.css +1 -0
- package/dist/dashboard/_nuxt/pages.DbXUtV-_.css +1 -0
- package/dist/dashboard/_nuxt/release.CN2gX7ob.css +1 -0
- package/dist/dashboard/_nuxt/tests.BKp4ZmZa.css +1 -0
- package/dist/dashboard/_nuxt/u4OLurSP.js +1 -0
- package/dist/dashboard/doctor/index.html +1 -0
- package/dist/dashboard/index.html +1 -0
- package/dist/dashboard/runs/new/index.html +1 -0
- package/dist/exec-S73NAGZS.js +11 -0
- package/dist/exec-S73NAGZS.js.map +1 -0
- package/dist/main.js +16779 -0
- package/dist/main.js.map +1 -0
- package/package.json +60 -33
- package/.editorconfig +0 -9
- package/.github/workflows/ci.yml +0 -124
- package/.nvmrc +0 -1
- package/.prettierrc +0 -6
- package/CLAUDE.md +0 -162
- package/PRODUCT.md +0 -63
- package/README.plain.md +0 -294
- package/apps/cli/README.md +0 -83
- package/apps/cli/package.json +0 -69
- package/apps/cli/scripts/bundle-dashboard.mjs +0 -18
- package/apps/cli/src/commands/control.ts +0 -115
- package/apps/cli/src/commands/doctor.ts +0 -37
- package/apps/cli/src/commands/port.ts +0 -168
- package/apps/cli/src/commands/runs.ts +0 -18
- package/apps/cli/src/commands/serve.ts +0 -33
- package/apps/cli/src/commands/setup.ts +0 -133
- package/apps/cli/src/commands/shared.ts +0 -35
- package/apps/cli/src/main.ts +0 -37
- package/apps/cli/src/version.ts +0 -16
- package/apps/cli/test/deps.test.ts +0 -39
- package/apps/cli/test/version.test.ts +0 -14
- package/apps/cli/tsconfig.json +0 -4
- package/apps/cli/tsup.config.ts +0 -21
- package/apps/dashboard/.impeccable/surfaces/apps-dashboard.md +0 -24
- package/apps/dashboard/app/app.vue +0 -5
- package/apps/dashboard/app/assets/board.css +0 -980
- package/apps/dashboard/app/assets/main.css +0 -1416
- package/apps/dashboard/app/assets/review.css +0 -379
- package/apps/dashboard/app/components/AppMark.vue +0 -15
- package/apps/dashboard/app/components/BoardHero.vue +0 -18
- package/apps/dashboard/app/components/BoardProjects.vue +0 -112
- package/apps/dashboard/app/components/BoardRunOverview.vue +0 -191
- package/apps/dashboard/app/components/BoardTestSummary.vue +0 -98
- package/apps/dashboard/app/components/BoardThemeToggle.vue +0 -26
- package/apps/dashboard/app/components/BudgetTape.vue +0 -43
- package/apps/dashboard/app/components/DashboardStyleSwitcher.vue +0 -35
- package/apps/dashboard/app/components/DesignMockup.vue +0 -329
- package/apps/dashboard/app/components/DesignReview.vue +0 -429
- package/apps/dashboard/app/components/DoctorView.vue +0 -237
- package/apps/dashboard/app/components/NetlifySetup.vue +0 -180
- package/apps/dashboard/app/components/PanelTitle.vue +0 -14
- package/apps/dashboard/app/components/PreviewAnnotator.vue +0 -273
- package/apps/dashboard/app/components/ProjectChat.vue +0 -392
- package/apps/dashboard/app/components/ReviewMarks.vue +0 -42
- package/apps/dashboard/app/components/RunTimeline.vue +0 -137
- package/apps/dashboard/app/components/StatusAnnunciator.vue +0 -14
- package/apps/dashboard/app/components/StatusLamp.vue +0 -12
- package/apps/dashboard/app/components/TranscriptList.vue +0 -69
- package/apps/dashboard/app/components/UiIcon.vue +0 -84
- package/apps/dashboard/app/composables/useApi.ts +0 -21
- package/apps/dashboard/app/composables/useBoardTheme.ts +0 -34
- package/apps/dashboard/app/composables/useClock.ts +0 -11
- package/apps/dashboard/app/composables/useCore.ts +0 -97
- package/apps/dashboard/app/composables/useDashboardStyle.ts +0 -21
- package/apps/dashboard/app/composables/useHealth.ts +0 -32
- package/apps/dashboard/app/composables/useProjectReview.ts +0 -140
- package/apps/dashboard/app/composables/useRunView.ts +0 -72
- package/apps/dashboard/app/layouts/default.vue +0 -185
- package/apps/dashboard/app/pages/doctor.vue +0 -3
- package/apps/dashboard/app/pages/index.vue +0 -245
- package/apps/dashboard/app/pages/runs/[id]/core.vue +0 -144
- package/apps/dashboard/app/pages/runs/[id]/critic.vue +0 -108
- package/apps/dashboard/app/pages/runs/[id]/doctor.vue +0 -17
- package/apps/dashboard/app/pages/runs/[id]/index.vue +0 -562
- package/apps/dashboard/app/pages/runs/[id]/plan.vue +0 -252
- package/apps/dashboard/app/pages/runs/[id]/release.vue +0 -488
- package/apps/dashboard/app/pages/runs/[id]/spec.vue +0 -309
- package/apps/dashboard/app/pages/runs/[id]/tests.vue +0 -323
- package/apps/dashboard/app/pages/runs/new.vue +0 -596
- package/apps/dashboard/app/plugins/webmcp.client.ts +0 -42
- package/apps/dashboard/app/utils/board.ts +0 -101
- package/apps/dashboard/app/utils/format.ts +0 -116
- package/apps/dashboard/app/utils/reviewTools.ts +0 -447
- package/apps/dashboard/app/utils/signals.ts +0 -34
- package/apps/dashboard/nuxt.config.ts +0 -35
- package/apps/dashboard/package.json +0 -25
- package/apps/dashboard/tsconfig.json +0 -3
- package/apps/server/package.json +0 -26
- package/apps/server/src/app.ts +0 -208
- package/apps/server/src/daemon.ts +0 -148
- package/apps/server/src/index.ts +0 -11
- package/apps/server/src/review.ts +0 -152
- package/apps/server/test/app.test.ts +0 -144
- package/apps/server/test/review.test.ts +0 -587
- package/apps/server/tsconfig.json +0 -4
- package/apps/server/tsup.config.ts +0 -12
- package/docs/ABI.md +0 -76
- package/docs/ADAPTERS.md +0 -74
- package/docs/ARCHITECTURE.md +0 -403
- package/docs/HOSTED-SEAM.md +0 -83
- package/docs/PROJECT-REVIEW.md +0 -56
- package/docs/evals/project-review.json +0 -171
- package/eslint.config.js +0 -55
- package/fixtures/inhouse/android-views-java-notes/README.md +0 -17
- package/fixtures/inhouse/android-views-java-notes/app/build.gradle +0 -31
- package/fixtures/inhouse/android-views-java-notes/app/src/main/AndroidManifest.xml +0 -21
- package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/MainActivity.java +0 -113
- package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/Note.java +0 -44
- package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/NoteEditorActivity.java +0 -52
- package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/NoteStore.java +0 -131
- package/fixtures/inhouse/android-views-java-notes/app/src/main/java/dev/multiplat/fixtures/notes/PrefsStorage.java +0 -23
- package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/activity_main.xml +0 -40
- package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/activity_note_editor.xml +0 -42
- package/fixtures/inhouse/android-views-java-notes/app/src/main/res/layout/item_note.xml +0 -20
- package/fixtures/inhouse/android-views-java-notes/app/src/main/res/values/strings.xml +0 -13
- package/fixtures/inhouse/android-views-java-notes/app/src/main/res/values/themes.xml +0 -4
- package/fixtures/inhouse/android-views-java-notes/app/src/test/java/dev/multiplat/fixtures/notes/NoteStoreTest.java +0 -76
- package/fixtures/inhouse/android-views-java-notes/build.gradle +0 -3
- package/fixtures/inhouse/android-views-java-notes/gradle.properties +0 -2
- package/fixtures/inhouse/android-views-java-notes/settings.gradle +0 -16
- package/fixtures/inhouse/ios-objc-notes/README.md +0 -16
- package/fixtures/inhouse/ios-objc-notes/Sources/AppDelegate.h +0 -4
- package/fixtures/inhouse/ios-objc-notes/Sources/AppDelegate.m +0 -18
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNote.h +0 -17
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNote.m +0 -54
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteEditorViewController.h +0 -8
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteEditorViewController.m +0 -91
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteStore.h +0 -27
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNoteStore.m +0 -131
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNotesListViewController.h +0 -8
- package/fixtures/inhouse/ios-objc-notes/Sources/MPFNotesListViewController.m +0 -98
- package/fixtures/inhouse/ios-objc-notes/Sources/SceneDelegate.h +0 -5
- package/fixtures/inhouse/ios-objc-notes/Sources/SceneDelegate.m +0 -17
- package/fixtures/inhouse/ios-objc-notes/Sources/main.m +0 -8
- package/fixtures/inhouse/ios-objc-notes/Tests/MPFNoteStoreTests.m +0 -54
- package/fixtures/inhouse/ios-objc-notes/project.yml +0 -48
- package/fixtures/inhouse/ios-uikit-notes/README.md +0 -19
- package/fixtures/inhouse/ios-uikit-notes/Sources/AppDelegate.swift +0 -21
- package/fixtures/inhouse/ios-uikit-notes/Sources/Note.swift +0 -8
- package/fixtures/inhouse/ios-uikit-notes/Sources/NoteEditorViewController.swift +0 -84
- package/fixtures/inhouse/ios-uikit-notes/Sources/NoteStore.swift +0 -95
- package/fixtures/inhouse/ios-uikit-notes/Sources/NotesListViewController.swift +0 -89
- package/fixtures/inhouse/ios-uikit-notes/Sources/SceneDelegate.swift +0 -14
- package/fixtures/inhouse/ios-uikit-notes/Tests/NoteStoreTests.swift +0 -49
- package/fixtures/inhouse/ios-uikit-notes/project.yml +0 -44
- package/fixtures/inhouse/web-vanilla-cart/README.md +0 -13
- package/fixtures/inhouse/web-vanilla-cart/index.html +0 -21
- package/fixtures/inhouse/web-vanilla-cart/main.js +0 -186
- package/fixtures/inhouse/web-vanilla-cart/pricing.js +0 -178
- package/fixtures/inhouse/web-vanilla-cart/styles.css +0 -60
- package/fixtures/inhouse/web-vanilla-counter/README.md +0 -12
- package/fixtures/inhouse/web-vanilla-counter/index.html +0 -20
- package/fixtures/inhouse/web-vanilla-counter/main.js +0 -106
- package/fixtures/inhouse/web-vanilla-counter/styles.css +0 -58
- package/fixtures/manifest.json +0 -175
- package/packages/adapters/package.json +0 -23
- package/packages/adapters/src/core/android.ts +0 -37
- package/packages/adapters/src/core/embed.ts +0 -96
- package/packages/adapters/src/core/ios.ts +0 -44
- package/packages/adapters/src/core/kotlin.ts +0 -208
- package/packages/adapters/src/core/swift.ts +0 -232
- package/packages/adapters/src/core/ts.ts +0 -217
- package/packages/adapters/src/core/web.ts +0 -24
- package/packages/adapters/src/index.ts +0 -118
- package/packages/adapters/src/registry.ts +0 -46
- package/packages/adapters/src/source/cross.ts +0 -68
- package/packages/adapters/src/source/mobile.ts +0 -618
- package/packages/adapters/src/source/pm.ts +0 -72
- package/packages/adapters/src/source/web.ts +0 -541
- package/packages/adapters/src/target/android/index.ts +0 -466
- package/packages/adapters/src/target/android/template.ts +0 -517
- package/packages/adapters/src/target/gradle.ts +0 -84
- package/packages/adapters/src/target/ios/index.ts +0 -491
- package/packages/adapters/src/target/ios/template.ts +0 -368
- package/packages/adapters/src/target/junit.ts +0 -93
- package/packages/adapters/src/target/maestro-evidence.ts +0 -283
- package/packages/adapters/src/target/maestro.ts +0 -925
- package/packages/adapters/src/target/registry.ts +0 -11
- package/packages/adapters/src/target/start-screen.ts +0 -25
- package/packages/adapters/src/target/web/executor.ts +0 -812
- package/packages/adapters/src/target/web/index.ts +0 -456
- package/packages/adapters/src/target/web/template.ts +0 -609
- package/packages/adapters/src/target/xcodebuild.ts +0 -90
- package/packages/adapters/src/types.ts +0 -310
- package/packages/adapters/test/core-embed.test.ts +0 -279
- package/packages/adapters/test/detect.test.ts +0 -144
- package/packages/adapters/test/executor.test.ts +0 -726
- package/packages/adapters/test/fixture-matrix.test.ts +0 -29
- package/packages/adapters/test/generic-observe.test.ts +0 -89
- package/packages/adapters/test/ios.test.ts +0 -93
- package/packages/adapters/test/maestro-evidence.test.ts +0 -460
- package/packages/adapters/test/source-tests.test.ts +0 -138
- package/packages/adapters/test/target.test.ts +0 -1531
- package/packages/adapters/test/web-flavors.test.ts +0 -100
- package/packages/adapters/test/web.test.ts +0 -74
- package/packages/adapters/tsconfig.json +0 -4
- package/packages/analyzer/package.json +0 -27
- package/packages/analyzer/src/candidacy.ts +0 -61
- package/packages/analyzer/src/index.ts +0 -39
- package/packages/analyzer/src/inventory.ts +0 -671
- package/packages/analyzer/src/observe/web.ts +0 -12
- package/packages/analyzer/src/prompts.ts +0 -240
- package/packages/analyzer/src/stages/behaviors.ts +0 -130
- package/packages/analyzer/src/stages/ingest.ts +0 -161
- package/packages/analyzer/src/stages/inventory.ts +0 -41
- package/packages/analyzer/src/stages/observe.ts +0 -439
- package/packages/analyzer/src/stages/plan.ts +0 -147
- package/packages/analyzer/src/stages/spec.ts +0 -221
- package/packages/analyzer/src/validate.ts +0 -117
- package/packages/analyzer/src/worker.ts +0 -190
- package/packages/analyzer/test/duplicates.test.ts +0 -98
- package/packages/analyzer/test/inventory.test.ts +0 -161
- package/packages/analyzer/test/observe.test.ts +0 -54
- package/packages/analyzer/test/prompts.test.ts +0 -129
- package/packages/analyzer/test/source-tests.test.ts +0 -117
- package/packages/analyzer/test/validate-routes.test.ts +0 -51
- package/packages/analyzer/test/validate.test.ts +0 -228
- package/packages/analyzer/test/worker.test.ts +0 -151
- package/packages/analyzer/tsconfig.json +0 -4
- package/packages/core/package.json +0 -32
- package/packages/core/src/config.ts +0 -68
- package/packages/core/src/cost/prices.json +0 -16
- package/packages/core/src/cost/prices.ts +0 -34
- package/packages/core/src/events/bus.ts +0 -49
- package/packages/core/src/ids.ts +0 -9
- package/packages/core/src/index.ts +0 -9
- package/packages/core/src/paths.ts +0 -35
- package/packages/core/src/reducer.ts +0 -149
- package/packages/core/src/schemas/appspec.ts +0 -159
- package/packages/core/src/schemas/artifact.ts +0 -42
- package/packages/core/src/schemas/behaviorspec.ts +0 -96
- package/packages/core/src/schemas/common.ts +0 -29
- package/packages/core/src/schemas/config.ts +0 -47
- package/packages/core/src/schemas/corevectors.ts +0 -71
- package/packages/core/src/schemas/design.ts +0 -40
- package/packages/core/src/schemas/event.ts +0 -99
- package/packages/core/src/schemas/index.ts +0 -34
- package/packages/core/src/schemas/inventory.ts +0 -76
- package/packages/core/src/schemas/portplan.ts +0 -67
- package/packages/core/src/schemas/review.ts +0 -135
- package/packages/core/src/schemas/run.ts +0 -90
- package/packages/core/src/schemas/stage.ts +0 -97
- package/packages/core/src/schemas/testresult.ts +0 -47
- package/packages/core/src/schemas/toolchain.ts +0 -32
- package/packages/core/src/schemas/verdict.ts +0 -43
- package/packages/core/src/stages/catalog.ts +0 -139
- package/packages/core/src/store/index.ts +0 -2
- package/packages/core/src/store/sqlite.ts +0 -185
- package/packages/core/src/store/types.ts +0 -27
- package/packages/core/test/reducer.test.ts +0 -101
- package/packages/core/test/routes.test.ts +0 -23
- package/packages/core/test/schemas.test.ts +0 -86
- package/packages/core/test/store-events.test.ts +0 -36
- package/packages/core/test/store-shared.test.ts +0 -17
- package/packages/core/test/store.test.ts +0 -105
- package/packages/core/tsconfig.json +0 -4
- package/packages/critic/package.json +0 -21
- package/packages/critic/src/decide.ts +0 -115
- package/packages/critic/src/index.ts +0 -12
- package/packages/critic/src/prompt.ts +0 -140
- package/packages/critic/src/rubrics.ts +0 -73
- package/packages/critic/test/decide.test.ts +0 -121
- package/packages/critic/tsconfig.json +0 -4
- package/packages/harness/package.json +0 -23
- package/packages/harness/src/devices/android.ts +0 -498
- package/packages/harness/src/devices/ios.ts +0 -356
- package/packages/harness/src/devices/maestro.ts +0 -32
- package/packages/harness/src/devices/remember.ts +0 -96
- package/packages/harness/src/devserver.ts +0 -222
- package/packages/harness/src/exec.ts +0 -97
- package/packages/harness/src/index.ts +0 -81
- package/packages/harness/src/lanelock.ts +0 -237
- package/packages/harness/src/lanes.ts +0 -117
- package/packages/harness/src/stage.ts +0 -70
- package/packages/harness/src/toolchain/checks/android.ts +0 -297
- package/packages/harness/src/toolchain/checks/basics.ts +0 -112
- package/packages/harness/src/toolchain/checks/llm.ts +0 -90
- package/packages/harness/src/toolchain/checks/playwright.ts +0 -154
- package/packages/harness/src/toolchain/checks/xcode.ts +0 -67
- package/packages/harness/src/toolchain/doctor.ts +0 -84
- package/packages/harness/src/toolchain/rust.ts +0 -47
- package/packages/harness/src/toolchain/types.ts +0 -36
- package/packages/harness/test/devices.test.ts +0 -281
- package/packages/harness/test/devserver.test.ts +0 -73
- package/packages/harness/test/doctor.test.ts +0 -82
- package/packages/harness/test/gradle-jdk.test.ts +0 -42
- package/packages/harness/test/ios-dismiss.test.ts +0 -31
- package/packages/harness/test/jdk-drop-in.test.ts +0 -40
- package/packages/harness/test/lanes.test.ts +0 -249
- package/packages/harness/test/playwright.test.ts +0 -183
- package/packages/harness/test/remember.test.ts +0 -55
- package/packages/harness/tsconfig.json +0 -4
- package/packages/llm/package.json +0 -23
- package/packages/llm/src/agent.ts +0 -219
- package/packages/llm/src/backend.ts +0 -20
- package/packages/llm/src/fake/recorder.ts +0 -20
- package/packages/llm/src/index.ts +0 -11
- package/packages/llm/src/messages.ts +0 -66
- package/packages/llm/src/models.ts +0 -15
- package/packages/llm/src/permissions.ts +0 -210
- package/packages/llm/test/permissions.test.ts +0 -191
- package/packages/llm/tsconfig.json +0 -4
- package/packages/orchestrator/package.json +0 -27
- package/packages/orchestrator/src/design-review.ts +0 -69
- package/packages/orchestrator/src/graph.ts +0 -69
- package/packages/orchestrator/src/impeccable.ts +0 -59
- package/packages/orchestrator/src/index.ts +0 -20
- package/packages/orchestrator/src/machine.ts +0 -159
- package/packages/orchestrator/src/review.ts +0 -670
- package/packages/orchestrator/src/runner.ts +0 -701
- package/packages/orchestrator/src/stages/critic.ts +0 -324
- package/packages/orchestrator/src/stages/fake.ts +0 -167
- package/packages/orchestrator/src/stages/placeholders.ts +0 -69
- package/packages/orchestrator/src/stages/prompts.ts +0 -107
- package/packages/orchestrator/src/stages/real.ts +0 -39
- package/packages/orchestrator/src/stages/report.ts +0 -758
- package/packages/orchestrator/src/stages/target.ts +0 -607
- package/packages/orchestrator/src/types.ts +0 -1
- package/packages/orchestrator/test/cost.test.ts +0 -117
- package/packages/orchestrator/test/critic.test.ts +0 -286
- package/packages/orchestrator/test/driver-lock.test.ts +0 -38
- package/packages/orchestrator/test/graph.test.ts +0 -57
- package/packages/orchestrator/test/lanes.test.ts +0 -65
- package/packages/orchestrator/test/machine.test.ts +0 -137
- package/packages/orchestrator/test/real.test.ts +0 -342
- package/packages/orchestrator/test/report.test.ts +0 -468
- package/packages/orchestrator/test/runner.test.ts +0 -165
- package/packages/orchestrator/test/target.test.ts +0 -558
- package/packages/orchestrator/tsconfig.json +0 -4
- package/packages/wasm-core/package.json +0 -26
- package/packages/wasm-core/src/cargo.ts +0 -134
- package/packages/wasm-core/src/host.ts +0 -105
- package/packages/wasm-core/src/index.ts +0 -38
- package/packages/wasm-core/src/prompts.ts +0 -145
- package/packages/wasm-core/src/source-tools.ts +0 -81
- package/packages/wasm-core/src/stages.ts +0 -661
- package/packages/wasm-core/src/template.ts +0 -327
- package/packages/wasm-core/src/vectors.ts +0 -90
- package/packages/wasm-core/test/host.test.ts +0 -108
- package/packages/wasm-core/test/prompts.test.ts +0 -136
- package/packages/wasm-core/test/source-tools.test.ts +0 -79
- package/packages/wasm-core/test/template.test.ts +0 -114
- package/packages/wasm-core/tsconfig.json +0 -7
- package/pnpm-workspace.yaml +0 -56
- package/scripts/compile-flows.ts +0 -65
- package/scripts/fixtures-sync.ts +0 -103
- package/scripts/release.ts +0 -55
- package/scripts/review-demo.ts +0 -81
- package/scripts/run-control.ts +0 -161
- package/scripts/spike-agent-sdk.ts +0 -37
- package/scripts/spike-target-scaffold.ts +0 -252
- package/tsconfig.base.json +0 -19
- package/turbo.json +0 -19
- package/vitest.config.ts +0 -8
- /package/{apps/dashboard/public → dist/dashboard}/board/plan.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/progress.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/projects.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/release.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/setup.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/tests.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/update.webp +0 -0
- /package/{apps/dashboard/public → dist/dashboard}/board/updatePlan.webp +0 -0
package/README.md
CHANGED
|
@@ -1,294 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# multiplat-factory
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Port an app from one platform (web, iOS, Android) to the others. Give the factory a source
|
|
4
|
+
project, pick the targets, and it analyzes the app, verifies its behaviors on the original (a dev
|
|
5
|
+
server plus Chromium for web, a simulator or emulator plus Maestro for mobile), plans the port,
|
|
6
|
+
generates native projects (SwiftUI, Jetpack Compose, or a web app in vanilla, React, Vue, or
|
|
7
|
+
Svelte), builds and tests them against the same behaviors, has an independent critic review the
|
|
8
|
+
result, and writes a `PORT.md` per target. Optionally it extracts the shared, non-UI logic into a
|
|
9
|
+
Rust core compiled to WebAssembly that runs unchanged on all three platforms with golden-vector
|
|
10
|
+
parity tests.
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
https://github.com/user-attachments/assets/145f78c3-7b22-4a36-b986-779a44c52e98
|
|
8
|
-
|
|
9
|
-
## How it works
|
|
10
|
-
|
|
11
|
-
The factory reads your app's code and works out what the app does: its screens, its controls, and the things a user can do with it (we call those **behaviors**). It then runs the original app and checks each behavior really works there, so it will infer what "correct" looks like. Next it plans the port, and Claude writes the code for each new platform. The factory builds that code, installs it on a simulator or emulator or in a browser, and runs the same behaviors against it. You can speak directly to the agent within the run as well.
|
|
12
|
-
|
|
13
|
-
Failures go back to the agent to fix, a limited number of times. A second, independent agent session reviews the finished port and scores it. Finally a `PORT.md` file per platform records what shipped, what passed, what failed, and what you chose to accept.
|
|
14
|
-
|
|
15
|
-
There are a couple of design principles:
|
|
16
|
-
|
|
17
|
-
- **An agent never decides what happens next.** The order of steps, the retry limits, the budget, and the pass or fail decisions are fixed code. The agent only writes files.
|
|
18
|
-
- **Everything is checked against the real app.** Behaviors are verified on the original before anything is written, and the ports are tested with real behaviors.
|
|
19
|
-
- **Leverage WASM for Portability** When your app has a good amount of logic that is not user interface (pricing rules, validation, data handling), the factory can pull that logic out into one **shared core**: a small Rust program compiled to WebAssembly that runs unchanged on web, iOS, and Android. It records the original app's real outputs for many inputs and checks the core gives the same answers on every platform.
|
|
20
|
-
|
|
21
|
-
A dashboard in your browser shows all of this live: where a run is, what it is spending, what is failing, and when it needs a decision from you.
|
|
22
|
-
|
|
23
|
-
## What it supports
|
|
24
|
-
|
|
25
|
-
| Your app is on | It can read | It produces |
|
|
26
|
-
| -------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
27
|
-
| Web | Vite, Next, Nuxt, SvelteKit, Angular, any npm project with build or dev scripts, or plain static HTML | A Vite app in vanilla JavaScript, React 19, Vue 3, or Svelte 5 (you choose, or the plan does) |
|
|
28
|
-
| iOS | Xcode projects and workspaces, XcodeGen `project.yml` files, Swift packages. Swift or Objective-C. UIKit or SwiftUI. | A SwiftUI app, built and tested on the iPhone simulator |
|
|
29
|
-
| Android | Gradle projects. Kotlin or Java. Views or Jetpack Compose. Multi-module and flavored builds are fine. | A Jetpack Compose app, built and tested on a headless emulator |
|
|
30
|
-
|
|
31
|
-
_Apps already built with a cross-platform framework (React Native, Flutter, Capacitor) are not supported_
|
|
32
|
-
|
|
33
|
-
## Install
|
|
34
|
-
|
|
35
|
-
The package is not on npm yet. Once it is:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm i -g multiplat-factory
|
|
39
|
-
mpf doctor
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
From a packed tarball (what the release script produces, see [Releasing and publishing](#releasing-and-publishing)):
|
|
12
|
+
## Quick start
|
|
43
13
|
|
|
44
14
|
```bash
|
|
45
|
-
npm i -g
|
|
46
|
-
mpf doctor
|
|
15
|
+
npm i -g multiplat-factory # or: npx multiplat-factory doctor
|
|
16
|
+
mpf doctor # what toolchains are present, what each gap disables (--json)
|
|
17
|
+
mpf setup # install what doctor reports missing, asking before each step (--yes, --dry-run)
|
|
18
|
+
mpf serve # the dashboard and API on http://127.0.0.1:4310 (--port, --host)
|
|
19
|
+
mpf port ./my-app --to ios,android --budget 30
|
|
20
|
+
mpf runs # runs so far; `mpf status <runId>` for one
|
|
47
21
|
```
|
|
48
22
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
git clone https://github.com/sdras/multiplat-factory.git
|
|
53
|
-
cd multiplat-factory
|
|
54
|
-
nvm use
|
|
55
|
-
pnpm install
|
|
56
|
-
pnpm mpf doctor
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Step by step instructions
|
|
60
|
-
|
|
61
|
-
1. **Check the machine.** `mpf doctor` prints one line per tool and says which platforms this machine can build for. `mpf doctor --json` prints the same report as JSON.
|
|
62
|
-
2. **Install what is missing.** `mpf setup` walks through the gaps doctor found and asks before running each install command (Homebrew on macOS, apt on Linux, winget on Windows). Anything that needs `sudo` runs only after you confirm that exact command. `mpf setup --yes` accepts every command that does not need sudo and prints the rest for you to run yourself. `mpf setup --dry-run` shows what would run and stops.
|
|
63
|
-
3. **Start the dashboard.** `mpf serve` runs the API and the dashboard on one address, `http://127.0.0.1:4310` by default (change it with `--port`, `--host`, or `MPF_PORT`). Leave it running. If you skip this, each command starts its own copy for as long as the command runs, so the dashboard disappears when the command ends.
|
|
64
|
-
4. **Port something.** `mpf port ./my-app --to ios,android` takes a folder or a git URL. It creates the run, prints a link to the run's dashboard page, and follows the run in the terminal until it finishes. The command exits with an error code unless the run succeeded or stopped where you told it to with `--until`. The first pause is plan approval: open the dashboard page, or run `mpf answer <runId> plan approve`.
|
|
65
|
-
5. **Read the result.** Each platform's project is in `~/.mpf/runs/<runId>/workspace/<target>/`. Its `PORT.md` lists what shipped, every behavior and whether its test passed, the reviewer's verdict, any deviations you accepted, and the parts of the shared core that were written by hand rather than recorded from the original. `report.json` in the run's folder holds the same data for tools. `mpf status <runId>` prints a one-line summary.
|
|
66
|
-
|
|
67
|
-
To rehearse the whole thing for free, with no Claude calls and no build tools:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
mpf port fixtures/inhouse/web-vanilla-counter --to web,android --fake --yes
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Commands
|
|
74
|
-
|
|
75
|
-
| Command | What it does |
|
|
76
|
-
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
77
|
-
| `mpf doctor [--json]` | Checks Node, git, the package manager, Claude access, Xcode, Java, the Android SDK, Gradle, Rust, Maestro, and Playwright's Chromium. Says which platforms you can build for and what each gap costs you. |
|
|
78
|
-
| `mpf setup [--yes] [--dry-run]` | Installs what doctor reports missing, asking before each step. |
|
|
79
|
-
| `mpf serve [--port <n>] [--host <h>]` | Runs the daemon (the API, the live event stream, and the dashboard) until you stop it. |
|
|
80
|
-
| `mpf port <source> --to <targets> [options]` | Ports a folder or git URL. Options are listed below. |
|
|
81
|
-
| `mpf runs` | Lists runs with their status, source, targets, and cost. |
|
|
82
|
-
| `mpf status <runId>` | Shows one run's stages and its summary. |
|
|
83
|
-
| `mpf resume <runId>` | Continues a paused run, including one that stopped at `--until`. |
|
|
84
|
-
| `mpf answer <runId> <stageId> <choice> [--note]` | Answers a stage that is waiting for you. Stage ids look like `plan`, `test:android`, or `critique:ios`. |
|
|
85
|
-
| `mpf rerun <runId> <stageId>` | Runs one stage again, plus everything after it. The rerun stages get a fresh allowance of fix rounds. |
|
|
86
|
-
| `mpf instruct <runId> <stageId> "<text>"` | Leaves a note for the Claude session that will work on that stage next. |
|
|
87
|
-
|
|
88
|
-
Options for `mpf port`:
|
|
89
|
-
|
|
90
|
-
- `--to web,ios,android`: the platforms to build.
|
|
91
|
-
- `--budget <usd>`: the spending cap. Default 30, at most 500.
|
|
92
|
-
- `--core auto|always|never`: whether to build the shared core. `auto` says no when only one platform is requested, since there is nothing to share.
|
|
93
|
-
- `--web-framework vanilla|react|vue|svelte`: the framework for a web port.
|
|
94
|
-
- `--until <stage>`: pause after that stage.
|
|
95
|
-
- `--yes`: skip the plan approval and the pause before fix rounds.
|
|
96
|
-
- `--detach`: create the run and return right away instead of following it.
|
|
97
|
-
- `--fake`: run the rehearsal pipeline with no Claude calls and no build tools.
|
|
98
|
-
|
|
99
|
-
A run's stages, in order (use these names for `--until` and in stage ids): `ingest`, `inventory`, `spec`, `behaviors`, `observe`, `plan`, `core.extract`, `core.vectors`, `core.build`, then for each platform `generate`, `build`, `test`, `critique`, `fix`, and finally `report`.
|
|
100
|
-
|
|
101
|
-
## What you need
|
|
102
|
-
|
|
103
|
-
- **Node 22.13 or newer.** The repo's `.nvmrc` asks for 24, so run `nvm use` in a source checkout. On an older Node, `pnpm` itself can fail before anything starts with the error `Cannot find matching keyid`. That is the old Corepack shim; switching Node fixes it.
|
|
104
|
-
- **A way to use agents.** Either set `ANTHROPIC_API_KEY`, or log in to Claude Code on this machine (`claude`, then `/login`). Every call goes through your account and every run has a spending cap.
|
|
105
|
-
- **The build tools for the platforms you want.** `mpf doctor` lists what is present. `mpf setup` installs missing build tools. In short:
|
|
106
|
-
- Web needs Playwright's Chromium, so the factory can watch the original app run.
|
|
107
|
-
- Android needs a JDK between 17 and 24, the Android SDK with an emulator and an `android-36` system image, Gradle, and Maestro (the tool that taps and types on mobile devices).
|
|
108
|
-
- iOS needs macOS with Xcode, an iOS simulator, XcodeGen, and Maestro.
|
|
109
|
-
- A shared core needs Rust with the `wasm32-unknown-unknown` target.
|
|
110
|
-
|
|
111
|
-
A platform whose tools are missing is skipped, and the run explain this.
|
|
23
|
+
Requires Node 22.13 or newer. The factory uses your own Claude credentials: `ANTHROPIC_API_KEY`,
|
|
24
|
+
or the Claude Code login on this machine. iOS targets need macOS with Xcode; web and Android
|
|
25
|
+
targets also run on Linux. A target whose toolchain is missing is skipped with a plain reason.
|
|
112
26
|
|
|
113
|
-
|
|
27
|
+
The dashboard runs on your machine next to the daemon, because the factory needs this machine's
|
|
28
|
+
simulators, emulators, toolchains, and Claude login. There is nothing to host. Commands start an
|
|
29
|
+
embedded daemon for their own lifetime when `mpf serve` is not running.
|
|
114
30
|
|
|
115
|
-
##
|
|
31
|
+
## Porting
|
|
116
32
|
|
|
117
|
-
|
|
33
|
+
`mpf port <path or git URL> --to web,ios,android` creates a run, prints its dashboard link, and
|
|
34
|
+
follows it in the terminal. Options: `--budget <usd>` (default 30, at most 500; the run pauses
|
|
35
|
+
when it is reached), `--core auto|always|never` for the shared WASM core (`auto` declines when
|
|
36
|
+
only one target is requested), `--web-framework vanilla|react|vue|svelte`, `--until <stage>`
|
|
37
|
+
(pause after that stage, for example `plan`), `--yes` (skip the plan approval and fix-loop
|
|
38
|
+
checkpoints), `--detach` (create the run and return), and `--fake` (a pipeline with no LLM and
|
|
39
|
+
no toolchains, to exercise the factory). Without `--detach` it exits non-zero unless the run
|
|
40
|
+
succeeded or stopped where `--until` asked.
|
|
118
41
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
42
|
+
Stage kinds, in order: `ingest`, `inventory`, `spec`, `behaviors`, `observe`, `plan`,
|
|
43
|
+
`core.extract`, `core.vectors`, `core.build`, then per target `generate`, `build`, `test`,
|
|
44
|
+
`critique`, `fix`, and finally `report`. Stage ids look like `plan`, `test:android`, or
|
|
45
|
+
`critique:ios`.
|
|
122
46
|
|
|
123
|
-
|
|
47
|
+
Each target's project lands in `~/.mpf/runs/<runId>/workspace/<target>/` with its `PORT.md`:
|
|
48
|
+
what shipped, every behavior and whether its flow passed, the critic's verdict, and any accepted
|
|
49
|
+
deviations.
|
|
124
50
|
|
|
125
|
-
|
|
51
|
+
## Checkpoints
|
|
126
52
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- `/`: all runs.
|
|
134
|
-
- `/runs/new`: start a new run.
|
|
135
|
-
- `/runs/<id>`: one run, with its stage graph, live transcript, budget, and controls.
|
|
136
|
-
- `spec`: the app's screens and behaviors, marked with which ones were verified on the original.
|
|
137
|
-
- `plan`: the port plan, with approve and reject.
|
|
138
|
-
- `tests`: results per platform, with flows, screenshots, failure evidence, and the shared-core parity table.
|
|
139
|
-
- `critic`: the independent review's verdicts and findings.
|
|
140
|
-
- `core`: a playground for the shared core.
|
|
141
|
-
- `release`: a release checklist.
|
|
142
|
-
- `doctor`: the tool report.
|
|
143
|
-
|
|
144
|
-
The dashboard also has review tools for a project: a chat about the project that can propose changes, a design review that drafts three concept mockups to pick from, annotation of recorded screenshots with pins and notes, and the release checklist. Applying a proposal creates a new version of the project and reruns the stages it touches. These actions are also available to browser agents as WebMCP tools on the page. `docs/PROJECT-REVIEW.md` describes all of it, and `pnpm tsx scripts/review-demo.ts <screenshot.png> <port>` runs a demo against a throwaway database. The "Host on Netlify" card on the release page is for the generated web app, not for the factory itself.
|
|
145
|
-
|
|
146
|
-
When developing from source, the dashboard is not prebuilt: run the API with `pnpm mpf serve` on 4310 and the dashboard with `pnpm dev`, which reloads on changes at `http://localhost:4311` and forwards `/api` calls to the daemon. Use `localhost`, not `127.0.0.1`; the dev server listens on IPv6.
|
|
147
|
-
|
|
148
|
-
## Where data lives
|
|
149
|
-
|
|
150
|
-
Run data never goes inside your project. The data folder is `$MPF_HOME` if set, else `$XDG_DATA_HOME/mpf`, else `~/.mpf`. Inside:
|
|
151
|
-
|
|
152
|
-
- `mpf.sqlite`: the run database. Everything that happened is stored as a log of events, and every view is rebuilt from that log.
|
|
153
|
-
- `daemon.json`: the running daemon's address, so the other commands can find it.
|
|
154
|
-
- `runs/<runId>/`: one folder per run:
|
|
155
|
-
- `source/`: a copy of the original app
|
|
156
|
-
- `detection.json`, `inventory.json`, `appspec.json`, `behaviorspec.json`, `portplan.json`: the analysis and the plan
|
|
157
|
-
- `observe/`: screenshots of the original app and its own test results
|
|
158
|
-
- `core/`: the shared core's Rust code, the built WebAssembly module, and the recorded inputs and outputs in `vectors/*.json`
|
|
159
|
-
- `workspace/<target>/`: the generated project, its `PORT.md`, and the test flows with their evidence
|
|
160
|
-
- `report.json`: the run's report
|
|
161
|
-
- `review/`: screenshot annotations
|
|
162
|
-
- `locks/`: one lock per run and one per device. Only one process drives a run at a time, and only one process uses the simulator or the emulator at a time.
|
|
163
|
-
- `jdks/`: JDKs you unpack here are found like installed ones. Some older apps come with a Gradle wrapper that needs an old JDK (Gradle 4 to 7 want JDK 8 to 16), and this lets you provide one without a package manager or sudo.
|
|
164
|
-
- `cache/`.
|
|
165
|
-
|
|
166
|
-
## Configuration
|
|
167
|
-
|
|
168
|
-
Settings come in layers, and later layers win: built-in defaults, then `config.json` in the data folder, then `mpf.config.ts`, `.js`, or `.json` in the current folder, then environment variables.
|
|
169
|
-
|
|
170
|
-
```json
|
|
171
|
-
{
|
|
172
|
-
"models": { "generate": "claude-opus-5", "critic": "claude-opus-5", "cheap": "claude-sonnet-5" },
|
|
173
|
-
"effort": { "generate": "xhigh", "critic": "high" },
|
|
174
|
-
"budget": { "defaultUsd": 30 },
|
|
175
|
-
"concurrency": {
|
|
176
|
-
"agents": 3,
|
|
177
|
-
"lanes": { "xcodebuild": 1, "gradle": 1, "simulator": 1, "emulator": 1, "web": 2 }
|
|
178
|
-
},
|
|
179
|
-
"toolchain": {
|
|
180
|
-
"androidHome": "/path/to/android-sdk",
|
|
181
|
-
"javaHome": "/path/to/jdk",
|
|
182
|
-
"developerDir": "/Applications/Xcode.app/Contents/Developer",
|
|
183
|
-
"maestro": "/path/to/maestro",
|
|
184
|
-
"cargo": "/path/to/cargo"
|
|
185
|
-
},
|
|
186
|
-
"server": { "host": "127.0.0.1", "port": 4310 },
|
|
187
|
-
"dataDir": "/somewhere/else"
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
The values shown are the defaults, except the `toolchain` entries. Those are empty by default because the tools are found automatically (`DEVELOPER_DIR` or `xcode-select`, `JAVA_HOME`, `ANDROID_HOME`, Homebrew folders, PATH). Fill one in only when discovery picks the wrong thing.
|
|
192
|
-
|
|
193
|
-
Environment variables:
|
|
194
|
-
|
|
195
|
-
- `MPF_HOME`: the data folder
|
|
196
|
-
- `MPF_PORT` and `MPF_HOST`: the daemon's address
|
|
197
|
-
- `MPF_BUDGET_USD`: the default budget
|
|
198
|
-
- `MPF_MODEL_GENERATE`, `MPF_MODEL_CRITIC`, `MPF_MODEL_CHEAP`: which Claude models to use
|
|
199
|
-
- `ANDROID_HOME` or `ANDROID_SDK_ROOT`, `JAVA_HOME`, `DEVELOPER_DIR`: where the build tools are
|
|
200
|
-
- `ANTHROPIC_API_KEY`: Claude access
|
|
201
|
-
- `MPF_ANDROID_AVD`: which emulator image to start. Otherwise the first one found, or `mpf-api36` if there is none.
|
|
202
|
-
- `MPF_KEEP_DEVICES=1`: leave the emulator or simulator running after a run that started it
|
|
203
|
-
- `PLAYWRIGHT_BROWSERS_PATH`: where Playwright keeps its browsers
|
|
204
|
-
|
|
205
|
-
## Run stages
|
|
206
|
-
|
|
207
|
-
1. `ingest` copies the app and works out its platform and framework.
|
|
208
|
-
2. `inventory` parses the code and collects the test ids the app already has, so tests can find its controls.
|
|
209
|
-
3. `spec` and `behaviors` describe the app in platform-neutral terms: its screens, controls, navigation, and the things a user can do.
|
|
210
|
-
4. `observe` runs the original app and checks each behavior against it, takes screenshots, and runs the app's own test suite as a baseline. Web apps run in Chromium from a dev server or a production build. iOS and Android apps run on a simulator or emulator, driven by Maestro.
|
|
211
|
-
5. `plan` decides how to build each port and whether a shared core is worth it.
|
|
212
|
-
6. If so, `core.extract`, `core.vectors`, and `core.build` pull out the shared logic, record the original app's real outputs for many inputs, and compile the core.
|
|
213
|
-
7. For each platform: the project is scaffolded, Claude writes the code, the factory builds it and runs the same behaviors against it, the independent reviewer scores it, and problems go back for fixing within fixed limits (three build-fix rounds, two test-fix rounds, two review rounds). A `PORT.md` sums it up.
|
|
214
|
-
8. `report` runs once every platform has finished, however it finished.
|
|
215
|
-
|
|
216
|
-
Devices are handled for you. The factory starts a headless emulator or an iPhone simulator when none is running, reuses one that is, clears stale system dialogs before each launch, and shuts down the devices it started when the run ends.
|
|
217
|
-
|
|
218
|
-
## Troubleshooting
|
|
219
|
-
|
|
220
|
-
- **`mpf doctor` says there is no Claude login, but you are logged in.** nvm keeps global commands per Node version, so a `claude` installed under another Node is not on PATH. The factory still finds the login. Either install `claude` under this Node, or ignore the line.
|
|
221
|
-
- **Xcode is installed, but `xcode-select` points at the Command Line Tools.** The factory finds Xcode itself and keeps working. `sudo xcode-select -s /Applications/Xcode.app` makes Xcode the default for everything else too.
|
|
222
|
-
- **Gradle fails with `java.lang.IllegalArgumentException: 27` or similar.** The JDK is newer than Gradle supports. The factory's own builds need JDK 17 to 24. Homebrew's plain `openjdk` tracks the newest release, so install `openjdk@21` instead. Run `gradle --stop` after switching JDKs. The original app's own wrapper may need an older JDK still; the factory picks one by the wrapper's version, and a JDK unpacked under `<data folder>/jdks` counts.
|
|
223
|
-
- **`brew install maestro` installs the wrong thing.** The mobile testing tool is `brew trust mobile-dev-inc/tap && brew install --formula mobile-dev-inc/tap/maestro`.
|
|
224
|
-
- **Web observation says "unverified".** Playwright's Chromium is missing. Run `npx playwright install chromium` (from a source checkout, `pnpm --filter @mpf/analyzer exec playwright install chromium`). The port still runs; the behaviors just are not checked against the original.
|
|
225
|
-
- **Every iOS test fails at once.** A stale "Open in …?" dialog from an earlier launch is hiding the whole app. The factory dismisses it before each launch. If one is stuck, shut the simulator down.
|
|
226
|
-
- **Every Android test fails at once.** A stale system dialog (an "isn't responding" prompt, a crash dialog, or an "Open with" chooser) is covering the app. The factory detects and dismisses these. If an "Open with" chooser keeps coming back, uninstall leftover `app.mpf.*` packages from the emulator.
|
|
227
|
-
- **"already being driven by another process".** Only one process drives a run at a time. Stop the other one (a daemon or a `run-control` script) before resuming or rerunning here.
|
|
228
|
-
- **Tests die part-way when the machine is busy.** Maestro gives up without a report under heavy load. Keep the machine quiet during device runs.
|
|
229
|
-
- **The run paused on its budget.** Raise it as described under [Pauses, budgets, and stopping early](#pauses-budgets-and-stopping-early), then resume.
|
|
230
|
-
|
|
231
|
-
## Developing the factory
|
|
53
|
+
A run pauses for you at three checkpoints: plan approval (`approve` or `reject`), before the first
|
|
54
|
+
paid fix round of a build or test stage (`continue` or `reject`; `--yes` skips both of these), and
|
|
55
|
+
critic escalation, when the critic cannot settle a target within its rounds (`accept` turns the
|
|
56
|
+
remaining findings into accepted deviations recorded in `PORT.md`; `reject` cancels). Answer from
|
|
57
|
+
the dashboard or with the control commands:
|
|
232
58
|
|
|
233
59
|
```bash
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
pnpm test # vitest across every package; tests that need a missing tool skip themselves
|
|
239
|
-
pnpm typecheck
|
|
240
|
-
pnpm lint
|
|
241
|
-
pnpm format # Prettier; `pnpm format:check` is what CI runs
|
|
242
|
-
pnpm mpf <command> # run the CLI from source
|
|
60
|
+
mpf answer <runId> <stageId> <choice> [--note "..."] # e.g. mpf answer 01M... plan approve
|
|
61
|
+
mpf resume <runId> # continue a paused run, or past a --until stop
|
|
62
|
+
mpf rerun <runId> <stageId> # re-run one stage and everything after it
|
|
63
|
+
mpf instruct <runId> <stageId> "text" # queue an instruction for a stage's next worker
|
|
243
64
|
```
|
|
244
65
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
- `pnpm tsx scripts/run-control.ts <runId> resume|rerun <stageId>|answer <stageId> <choice> [note]|status` drives an existing run.
|
|
248
|
-
- `resume` also takes `--until <stage>`, `--budget <usd>`, and `--core auto|always|never`. `rerun` takes `--approve`.
|
|
249
|
-
- `pnpm tsx scripts/run-control.ts new <source> --to ios,android [--until <stage>] [--budget <usd>] [--core …] [--yes]` creates a run in this process, on the current code.
|
|
250
|
-
|
|
251
|
-
Point `MPF_HOME` at the same folder the daemon serves and the run appears in the dashboard. Two more scripts help. `scripts/compile-flows.ts <runId> <target>` recompiles a platform's test flows from the stored behaviors without a device. `scripts/spike-target-scaffold.ts --target android|ios|web [--framework <flavor>] [--e2e] [--core]` scaffolds, builds, launches, runs one flow, and checks shared-core parity with no Claude calls.
|
|
252
|
-
|
|
253
|
-
**Fixtures** are the sample apps the factory is tested against. `fixtures/manifest.json` pins public apps by git URL, branch, and commit. `pnpm fixtures:sync` clones them into `fixtures/.cache` and fills in missing commits; `--id <fixture>` syncs one, and `--repin` moves the pins forward. The manifest also lists the in-house fixtures under `fixtures/inhouse/`:
|
|
254
|
-
|
|
255
|
-
- `web-vanilla-counter`: tiny; the shared core stays off
|
|
256
|
-
- `web-vanilla-cart`: about 150 lines of pure pricing logic, made for the shared core
|
|
257
|
-
- `ios-uikit-notes`: Swift, UIKit, XcodeGen spec, XCTest
|
|
258
|
-
- `ios-objc-notes`: Objective-C, XCTest
|
|
259
|
-
- `android-views-java-notes`: Java, XML layouts, JUnit
|
|
260
|
-
|
|
261
|
-
Every in-house fixture has a test id on every interactive element, and the mobile ones ship their own unit tests, which the `observe` stage records as the `source` result. `packages/adapters/test/detect.test.ts` checks the in-house fixtures, and `fixture-matrix.test.ts` checks the synced public ones.
|
|
262
|
-
|
|
263
|
-
**CI** (`.github/workflows/ci.yml`) runs on Linux and macOS: lint, the Prettier check, typecheck, the tests (with Rust and Chromium installed so the core and observe tests run), the rehearsal pipeline end to end, the dashboard build, and the bundled CLI. It also builds the four web flavors and runs one Playwright flow on each, builds the in-house mobile fixtures and runs their own tests, and syncs the public fixtures and checks they are detected correctly. Runs that use real Claude calls or real devices are not in CI.
|
|
264
|
-
|
|
265
|
-
To add a platform or framework, implement `SourceAdapter` or `TargetAdapter` from `packages/adapters/src/types.ts`, register it, and add a fixture. `docs/ADAPTERS.md` walks through it. `docs/ARCHITECTURE.md` describes the design and where the code differs from it. `docs/ABI.md` is the contract between the shared core and its hosts.
|
|
266
|
-
|
|
267
|
-
## Releasing and publishing
|
|
268
|
-
|
|
269
|
-
The published package is `multiplat-factory`. It is built from `apps/cli`, with every `@mpf/*` package bundled in and the static dashboard copied next to it. Its npm page is `apps/cli/README.md`.
|
|
270
|
-
|
|
271
|
-
1. Bump the version in `apps/cli/package.json`. The CLI reads it from there at run time, so that is the only place it lives.
|
|
272
|
-
2. Build and pack: `pnpm tsx scripts/release.ts`. It builds the dashboard, bundles the CLI, runs `mpf --help` from the bundle, and packs the tarball into `dist-release/`. It prints the tarball's contents (about 1.2 MB, with source maps).
|
|
273
|
-
3. Try the tarball in a clean project: `mkdir /tmp/try && cd /tmp/try && npm init -y && npm i <repo>/dist-release/multiplat-factory-<version>.tgz && npx mpf doctor && npx mpf serve`.
|
|
274
|
-
4. Log in to npm with `npm login` and confirm it with `npm whoami`. The name `multiplat-factory` is unscoped, so no organization is needed.
|
|
275
|
-
5. Publish: `pnpm tsx scripts/release.ts --publish`. It rebuilds, then runs `pnpm publish --access public --no-git-checks` from `apps/cli`. With two-factor auth on, npm asks for a one-time code.
|
|
276
|
-
6. Check from another machine: `npm view multiplat-factory version` and `npx multiplat-factory doctor`.
|
|
277
|
-
|
|
278
|
-
The package metadata and this README link to the GitHub repository. Make it public before publishing, or expect dead links.
|
|
66
|
+
## Data and configuration
|
|
279
67
|
|
|
280
|
-
|
|
68
|
+
Run data lives under `~/.mpf` (or `$MPF_HOME`, or `$XDG_DATA_HOME/mpf`), never inside your
|
|
69
|
+
project: the SQLite run store, `runs/<runId>/` with the copied source, the analysis, the
|
|
70
|
+
observation screenshots, the shared core, and each target's workspace, plus `jdks/` for JDKs you
|
|
71
|
+
unpack by hand for old Gradle wrappers.
|
|
281
72
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
- `packages/llm`: the Claude session wrapper, its sandbox rules, cost accounting, and a recorder
|
|
291
|
-
- `packages/critic`: the independent review
|
|
292
|
-
- `packages/wasm-core`: the Rust core template, its build runner, the Node host, the recorded vectors, and the core stages
|
|
73
|
+
Toolchains are discovered (`DEVELOPER_DIR` or `xcode-select`, `JAVA_HOME`, `ANDROID_HOME`,
|
|
74
|
+
Homebrew prefixes, PATH) with overrides in `~/.mpf/config.json`: `models` (`generate`, `critic`,
|
|
75
|
+
`cheap`), `effort`, `budget.defaultUsd`, `concurrency`, `toolchain` (`androidHome`, `javaHome`,
|
|
76
|
+
`developerDir`, `maestro`, `cargo`), `server` (`host`, `port`), and `dataDir`. Environment
|
|
77
|
+
variables override the file: `MPF_HOME`, `MPF_PORT`, `MPF_HOST`, `MPF_BUDGET_USD`,
|
|
78
|
+
`MPF_MODEL_GENERATE`, `MPF_MODEL_CRITIC`, `MPF_MODEL_CHEAP`, `ANDROID_HOME`, `JAVA_HOME`,
|
|
79
|
+
`DEVELOPER_DIR`, `MPF_ANDROID_AVD` (the emulator image to boot), and `MPF_KEEP_DEVICES=1` (leave
|
|
80
|
+
the devices a run booted running).
|
|
293
81
|
|
|
294
|
-
|
|
82
|
+
Source code, the full guide with troubleshooting, the architecture, and the adapter interface for
|
|
83
|
+
adding frameworks: https://github.com/sdras/multiplat-factory
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// ../../packages/harness/src/exec.ts
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
async function runProcess(opts) {
|
|
4
|
+
const started = Date.now();
|
|
5
|
+
const limit = opts.captureLimit ?? 2e6;
|
|
6
|
+
let out = "";
|
|
7
|
+
let err = "";
|
|
8
|
+
const execaOpts = {
|
|
9
|
+
cwd: opts.cwd,
|
|
10
|
+
env: opts.env,
|
|
11
|
+
reject: false,
|
|
12
|
+
timeout: opts.timeoutMs,
|
|
13
|
+
cancelSignal: opts.signal,
|
|
14
|
+
killSignal: "SIGTERM",
|
|
15
|
+
forceKillAfterDelay: 5e3,
|
|
16
|
+
cleanup: true,
|
|
17
|
+
stdin: "ignore",
|
|
18
|
+
stdout: "pipe",
|
|
19
|
+
stderr: "pipe"
|
|
20
|
+
};
|
|
21
|
+
const child = execa(opts.command, opts.args ?? [], execaOpts);
|
|
22
|
+
child.stdout?.on("data", (b) => {
|
|
23
|
+
const s = b.toString();
|
|
24
|
+
opts.onStdout?.(s);
|
|
25
|
+
if (out.length < limit) out += s;
|
|
26
|
+
});
|
|
27
|
+
child.stderr?.on("data", (b) => {
|
|
28
|
+
const s = b.toString();
|
|
29
|
+
opts.onStderr?.(s);
|
|
30
|
+
if (err.length < limit) err += s;
|
|
31
|
+
});
|
|
32
|
+
const result = await child;
|
|
33
|
+
return {
|
|
34
|
+
command: [opts.command, ...opts.args ?? []].join(" "),
|
|
35
|
+
exitCode: result.exitCode ?? null,
|
|
36
|
+
stdout: out,
|
|
37
|
+
stderr: err,
|
|
38
|
+
durationMs: Date.now() - started,
|
|
39
|
+
timedOut: Boolean(result.timedOut),
|
|
40
|
+
cancelled: Boolean(result.isCanceled)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function which(bin, env = process.env) {
|
|
44
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
45
|
+
const r = await runProcess({ command: cmd, args: [bin], env, timeoutMs: 5e3 });
|
|
46
|
+
if (r.exitCode !== 0) return void 0;
|
|
47
|
+
const first = r.stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
48
|
+
return first?.trim();
|
|
49
|
+
}
|
|
50
|
+
async function tryOutput(command, args = [], env = process.env, timeoutMs = 15e3) {
|
|
51
|
+
try {
|
|
52
|
+
const r = await runProcess({ command, args, env, timeoutMs });
|
|
53
|
+
if (r.exitCode !== 0) return void 0;
|
|
54
|
+
return r.stdout.trim();
|
|
55
|
+
} catch {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
runProcess,
|
|
62
|
+
which,
|
|
63
|
+
tryOutput
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-A5ZTNRTN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../packages/harness/src/exec.ts"],"sourcesContent":["import { execa, type Options as ExecaOptions } from 'execa'\n\nexport interface RunProcessOptions {\n command: string\n args?: string[]\n cwd?: string\n env?: NodeJS.ProcessEnv\n timeoutMs?: number\n signal?: AbortSignal\n onStdout?: (chunk: string) => void\n onStderr?: (chunk: string) => void\n /** Cap on captured output per stream, in characters. Streaming callbacks still see everything. */\n captureLimit?: number\n}\n\nexport interface ProcessResult {\n command: string\n exitCode: number | null\n stdout: string\n stderr: string\n durationMs: number\n timedOut: boolean\n cancelled: boolean\n}\n\n/**\n * Runs a child process with a timeout, kill-tree on abort, and streaming output.\n * Never throws for a non-zero exit; inspect `exitCode`.\n */\nexport async function runProcess(opts: RunProcessOptions): Promise<ProcessResult> {\n const started = Date.now()\n const limit = opts.captureLimit ?? 2_000_000\n let out = ''\n let err = ''\n const execaOpts: ExecaOptions = {\n cwd: opts.cwd,\n env: opts.env,\n reject: false,\n timeout: opts.timeoutMs,\n cancelSignal: opts.signal,\n killSignal: 'SIGTERM',\n forceKillAfterDelay: 5000,\n cleanup: true,\n stdin: 'ignore',\n stdout: 'pipe',\n stderr: 'pipe',\n }\n const child = execa(opts.command, opts.args ?? [], execaOpts)\n child.stdout?.on('data', (b: Buffer) => {\n const s = b.toString()\n opts.onStdout?.(s)\n if (out.length < limit) out += s\n })\n child.stderr?.on('data', (b: Buffer) => {\n const s = b.toString()\n opts.onStderr?.(s)\n if (err.length < limit) err += s\n })\n const result = await child\n return {\n command: [opts.command, ...(opts.args ?? [])].join(' '),\n exitCode: result.exitCode ?? null,\n stdout: out,\n stderr: err,\n durationMs: Date.now() - started,\n timedOut: Boolean(result.timedOut),\n cancelled: Boolean(result.isCanceled),\n }\n}\n\n/** Resolve a binary on PATH; returns undefined when absent. */\nexport async function which(\n bin: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<string | undefined> {\n const cmd = process.platform === 'win32' ? 'where' : 'which'\n const r = await runProcess({ command: cmd, args: [bin], env, timeoutMs: 5000 })\n if (r.exitCode !== 0) return undefined\n const first = r.stdout.split(/\\r?\\n/).find((l) => l.trim().length > 0)\n return first?.trim()\n}\n\n/** Run a command and return trimmed stdout, or undefined on any failure. */\nexport async function tryOutput(\n command: string,\n args: string[] = [],\n env: NodeJS.ProcessEnv = process.env,\n timeoutMs = 15_000,\n): Promise<string | undefined> {\n try {\n const r = await runProcess({ command, args, env, timeoutMs })\n if (r.exitCode !== 0) return undefined\n return r.stdout.trim()\n } catch {\n return undefined\n }\n}\n"],"mappings":";AAAA,SAAS,aAA2C;AA6BpD,eAAsB,WAAW,MAAiD;AAChF,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,QAAQ,KAAK,gBAAgB;AACnC,MAAI,MAAM;AACV,MAAI,MAAM;AACV,QAAM,YAA0B;AAAA,IAC9B,KAAK,KAAK;AAAA,IACV,KAAK,KAAK;AAAA,IACV,QAAQ;AAAA,IACR,SAAS,KAAK;AAAA,IACd,cAAc,KAAK;AAAA,IACnB,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,QAAQ,MAAM,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS;AAC5D,QAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAM,IAAI,EAAE,SAAS;AACrB,SAAK,WAAW,CAAC;AACjB,QAAI,IAAI,SAAS,MAAO,QAAO;AAAA,EACjC,CAAC;AACD,QAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAM,IAAI,EAAE,SAAS;AACrB,SAAK,WAAW,CAAC;AACjB,QAAI,IAAI,SAAS,MAAO,QAAO;AAAA,EACjC,CAAC;AACD,QAAM,SAAS,MAAM;AACrB,SAAO;AAAA,IACL,SAAS,CAAC,KAAK,SAAS,GAAI,KAAK,QAAQ,CAAC,CAAE,EAAE,KAAK,GAAG;AAAA,IACtD,UAAU,OAAO,YAAY;AAAA,IAC7B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,YAAY,KAAK,IAAI,IAAI;AAAA,IACzB,UAAU,QAAQ,OAAO,QAAQ;AAAA,IACjC,WAAW,QAAQ,OAAO,UAAU;AAAA,EACtC;AACF;AAGA,eAAsB,MACpB,KACA,MAAyB,QAAQ,KACJ;AAC7B,QAAM,MAAM,QAAQ,aAAa,UAAU,UAAU;AACrD,QAAM,IAAI,MAAM,WAAW,EAAE,SAAS,KAAK,MAAM,CAAC,GAAG,GAAG,KAAK,WAAW,IAAK,CAAC;AAC9E,MAAI,EAAE,aAAa,EAAG,QAAO;AAC7B,QAAM,QAAQ,EAAE,OAAO,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;AACrE,SAAO,OAAO,KAAK;AACrB;AAGA,eAAsB,UACpB,SACA,OAAiB,CAAC,GAClB,MAAyB,QAAQ,KACjC,YAAY,MACiB;AAC7B,MAAI;AACF,UAAM,IAAI,MAAM,WAAW,EAAE,SAAS,MAAM,KAAK,UAAU,CAAC;AAC5D,QAAI,EAAE,aAAa,EAAG,QAAO;AAC7B,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>multiplat-factory</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkcIzcLy.js"}}</script><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=B612:ital,wght@0,400;0,700;1,400&family=B612+Mono:wght@400;700&family=Big+Shoulders+Display:wght@500;600;700&family=Familjen+Grotesk:wght@400;500;600;700&display=swap"><link rel="stylesheet" href="/_nuxt/entry.DRmJCmhL.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkcIzcLy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BSEomhaZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CCMCJdY2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B1MQUUmA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BVtebpBS.js"><script type="module" src="/_nuxt/DkcIzcLy.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/_nuxt/default.BwIDAPER.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CFi3g9tC.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/G-IHUB_t.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D2giJ0zb.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BeCRcAqm.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DxWip8Ok.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZdpP1Ud-.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/C6vzBuMd.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Bq-TOqNj.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/dnzJofwx.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.xaLU7Cq-.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/RgvLL8ZL.js"><meta name="color-scheme" content="dark"><meta name="theme-color" content="#1a1917"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{apiBase:"/api/v1"},app:{baseURL:"/",buildId:"1d285342-32d1-4f36-aa29-aea4bf9510b7",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793581709535,false]</script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>multiplat-factory</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkcIzcLy.js"}}</script><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=B612:ital,wght@0,400;0,700;1,400&family=B612+Mono:wght@400;700&family=Big+Shoulders+Display:wght@500;600;700&family=Familjen+Grotesk:wght@400;500;600;700&display=swap"><link rel="stylesheet" href="/_nuxt/entry.DRmJCmhL.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkcIzcLy.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BSEomhaZ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CCMCJdY2.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B1MQUUmA.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BVtebpBS.js"><script type="module" src="/_nuxt/DkcIzcLy.js" crossorigin></script><link rel="prefetch" as="style" crossorigin href="/_nuxt/default.BwIDAPER.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/CFi3g9tC.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/G-IHUB_t.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/D2giJ0zb.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/BeCRcAqm.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DxWip8Ok.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZdpP1Ud-.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/C6vzBuMd.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.Bq-TOqNj.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/dnzJofwx.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.xaLU7Cq-.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/RgvLL8ZL.js"><meta name="color-scheme" content="dark"><meta name="theme-color" content="#1a1917"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{apiBase:"/api/v1"},app:{baseURL:"/",buildId:"1d285342-32d1-4f36-aa29-aea4bf9510b7",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793581709535,false]</script></body></html>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
function e(e){return e.startsWith(`before launching`)}function t(e){if(!e)return{rest:``};let t=e.split(`
|
|
2
|
+
`),n=t.findIndex(e=>e.startsWith(`A system dialog covers the app:`));return n===-1?{rest:e}:{dialog:t[n],rest:t.filter((e,t)=>t!==n).join(`
|
|
3
|
+
`)}}function n(e){let t=e.split(`. `),n=t.findIndex(e=>/\breject\b/.test(e));return n<=0?[e]:[`${t.slice(0,n).join(`. `)}.`,t.slice(n).join(`. `)]}export{n,t as r,e as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Jt as e,Xt as t,Yt as n,a as r,bt as i,ft as a}from"./BSEomhaZ.js";import"./CCMCJdY2.js";var o=t({docsBase:e,reporters:n}),s=`$s`;function c(...e){let t=typeof e[e.length-1]==`string`?e.pop():void 0;typeof e[0]!=`string`&&e.unshift(t);let[n,c]=e;if(!n||typeof n!=`string`)throw o.NUXT_E7009({key:n});if(c!==void 0&&typeof c!=`function`)throw o.NUXT_E7007({type:typeof c});let l=s+n,u=r(),d=i(u.payload.state,l);if(c&&(u._state[l]??={_default:c}),d.value===void 0&&c){let e=c();if(a(e))return u.payload.state[l]=e,e;d.value=e}return d}export{c as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{F as e,Gt as t,Ht as n,O as r,St as i,T as a,Z as o,w as s}from"./BSEomhaZ.js";import{f as c,o as l}from"./BeCRcAqm.js";var u=Object.assign(e({__name:`StatusLamp`,props:{status:{},label:{},strong:{type:Boolean},hollow:{type:Boolean}},setup(e){let u=e,d=s(()=>u.label??c(u.status));return(s,c)=>(o(),r(`span`,{class:n([`status`,`st-${e.status}`])},[a(`span`,{class:n([`lamp`,{live:(`isLive`in s?s.isLive:i(l))(e.status),hollow:e.hollow}])},null,2),a(`span`,{class:n({lbl:e.strong})},t(i(d)),3)],2))}}),{__name:`StatusLamp`});export{u as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{D as e,F as t,Gt as n,Ht as r,M as i,N as a,O as o,St as s,T as c,Y as l,Z as u,et as d,gt as f,w as p,y as m}from"./BSEomhaZ.js";import{t as h}from"./BVtebpBS.js";import{t as g}from"#entry";import{g as _}from"./BeCRcAqm.js";import{t as v}from"./BD1XFGRw.js";import{t as y}from"./DlF4qIFG.js";var b={class:`doctor-page`},x={class:`page-head`},S={key:0,class:`readouts`},C={class:`readout`},w={class:`v mono`},T={class:`readout`},E={class:`v row`,style:{gap:`14px`}},D={class:`readout`},O={class:`v mono`},k={class:`readout`},A={class:`v`},j=[`disabled`],M={key:0,class:`callout bad`,style:{"margin-bottom":`16px`}},N={class:`body`},P={key:1,class:`boxed`},F={class:`cell`,style:{"line-height":`1.35`}},I={style:{"font-size":`13.5px`,"font-weight":`700`}},L={key:0,class:`mono small muted`},R={class:`stack check-detail`,style:{gap:`6px`}},z={key:0,class:`fix`},B=[`aria-label`,`onClick`],V={class:`tags affects`},H={key:2,class:`muted`},U=Object.assign(g(t({__name:`DoctorView`,setup(t){let g=h(),U=f(),W=f(),G=f(!1),K=f(),{now:q}=y(5e3);async function J(e=!1){G.value=!0;try{U.value=await g.get(`/doctor${e?`?refresh=1`:``}`),W.value=void 0}catch(e){W.value=e.message}finally{G.value=!1}}l(()=>J());let Y=[{id:`web`,name:`web`},{id:`ios`,name:`iOS`},{id:`android`,name:`android`}],X=p(()=>new Set(U.value?.availableTargets??[])),Z=p(()=>{if(!U.value)return``;let e=Math.max(0,Math.round((q.value-new Date(U.value.generatedAt).getTime())/1e3));return e<60?`just now`:`${Math.round(e/60)} min ago`}),Q=e=>e.fix?.brew??e.fix?.apt??e.fix?.winget??e.fix?.manual;async function $(e,t){try{await navigator.clipboard.writeText(e),K.value=t,setTimeout(()=>K.value=void 0,1500)}catch{K.value=void 0}}return(t,l)=>{let f=v,p=_;return u(),o(`div`,b,[c(`div`,x,[l[5]||=c(`div`,null,[c(`h1`,{class:`title`},`Doctor`),c(`p`,{class:`subtitle`},[i(` Toolchains found on this machine and what each one unlocks. Nothing here is configured by hand; overrides live in `),c(`code`,null,`~/.mpf/config.json`),i(`. `)])],-1),l[6]||=c(`div`,{class:`grow`},null,-1),s(U)?(u(),o(`div`,S,[c(`div`,C,[l[1]||=c(`span`,{class:`k`},`Host`,-1),c(`span`,w,n(s(U).host.os)+` / `+n(s(U).host.arch)+` · node `+n(s(U).host.node),1)]),c(`div`,T,[l[2]||=c(`span`,{class:`k`},`Targets available`,-1),c(`span`,E,[(u(),o(m,null,d(Y,e=>a(f,{key:e.id,status:s(X).has(e.id)?`ok`:`missing`,label:e.name,hollow:!s(X).has(e.id)},null,8,[`status`,`label`,`hollow`])),64))])]),c(`div`,D,[l[3]||=c(`span`,{class:`k`},`Claude backend`,-1),c(`span`,O,n(s(U).llmBackend),1)]),c(`div`,k,[l[4]||=c(`span`,{class:`k`},`Checked`,-1),c(`span`,A,n(s(Z)),1)])])):e(``,!0),c(`button`,{class:`btn`,type:`button`,disabled:s(G),onClick:l[0]||=e=>J(!0)},[a(p,{name:`refresh`}),i(n(s(G)?`Checking`:`Re-check`),1)],8,j)]),s(W)?(u(),o(`div`,M,[l[10]||=c(`span`,{class:`lamp`},null,-1),c(`div`,null,[l[9]||=c(`div`,{class:`head`},`Cannot reach the server`,-1),c(`div`,N,[i(n(s(W))+`. Start it with `,1),l[7]||=c(`code`,null,`pnpm mpf serve`,-1),l[8]||=i(`.`,-1)])])])):e(``,!0),s(U)?(u(),o(`div`,P,[l[12]||=c(`div`,{class:`head-row check-head`},[c(`span`,null,`Status`),c(`span`,null,`Check`),c(`span`,null,`Detail · fix`),c(`span`,{class:`affects`},`Affects`)],-1),(u(!0),o(m,null,d(s(U).checks,t=>(u(),o(`div`,{key:t.id,class:`check`},[a(f,{status:t.status,strong:``,hollow:t.status!==`ok`},null,8,[`status`,`hollow`]),c(`div`,F,[c(`span`,I,n(t.label),1),t.version?(u(),o(`span`,L,n(t.version),1)):e(``,!0)]),c(`div`,R,[c(`span`,{style:{"font-size":`13px`,"line-height":`1.45`},class:r(t.status===`ok`?`dim`:``)},n(t.detail),3),t.status!==`ok`&&Q(t)?(u(),o(`div`,z,[c(`code`,null,n(Q(t)),1),c(`button`,{class:`btn tiny`,type:`button`,"aria-label":`Copy the fix for ${t.label}`,onClick:e=>$(Q(t),t.id)},[a(p,{name:`copy`,size:11}),i(n(s(K)===t.id?`Copied`:`Copy`),1)],8,B)])):e(``,!0)]),c(`div`,V,[l[11]||=c(`span`,{class:`affects-label muted small`},`Used for`,-1),(u(!0),o(m,null,d(t.affects,e=>(u(),o(`span`,{key:e,class:`tag`},n(e),1))),128))])]))),128))])):s(W)?e(``,!0):(u(),o(`p`,H,`Checking toolchains…`))])}}}),[[`__scopeId`,`data-v-f46173f9`]]),{__name:`DoctorView`});export{U as t};
|