apphx 0.0.0-stage → 1.0.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +181 -2
  3. package/dist/apple-types.js +45 -0
  4. package/dist/apple-types.js.map +1 -0
  5. package/dist/apple.js +900 -0
  6. package/dist/apple.js.map +1 -0
  7. package/dist/cli.js +414 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.js +80 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/dependencies.js +157 -0
  12. package/dist/dependencies.js.map +1 -0
  13. package/dist/export-options.js +49 -0
  14. package/dist/export-options.js.map +1 -0
  15. package/dist/flutter.js +171 -0
  16. package/dist/flutter.js.map +1 -0
  17. package/dist/inbox.js +128 -0
  18. package/dist/inbox.js.map +1 -0
  19. package/dist/integrations.js +435 -0
  20. package/dist/integrations.js.map +1 -0
  21. package/dist/preflight.js +43 -0
  22. package/dist/preflight.js.map +1 -0
  23. package/dist/process.js +260 -0
  24. package/dist/process.js.map +1 -0
  25. package/dist/remote.js +143 -0
  26. package/dist/remote.js.map +1 -0
  27. package/dist/requirements.js +37 -0
  28. package/dist/requirements.js.map +1 -0
  29. package/dist/runner.js +722 -0
  30. package/dist/runner.js.map +1 -0
  31. package/dist/scheduler.js +83 -0
  32. package/dist/scheduler.js.map +1 -0
  33. package/dist/schemas.js +232 -0
  34. package/dist/schemas.js.map +1 -0
  35. package/dist/store.js +361 -0
  36. package/dist/store.js.map +1 -0
  37. package/examples/factory.json +28 -0
  38. package/examples/luggage.json +39 -0
  39. package/examples/remote.json +6 -0
  40. package/package.json +30 -3
  41. package/skills/apple-app-factory/LICENSE +21 -0
  42. package/skills/apple-app-factory/SKILL.md +103 -0
  43. package/skills/apple-app-factory/resources/requirement.json +39 -0
  44. package/skills/apple-app-factory/resources/research-result.json +21 -0
  45. package/skills/apple-app-factory/resources/review-result.json +7 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Apple App Factory contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,182 @@
1
- # Temporary Holding Version
1
+ # Apple App Factory
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 给现有 AI agent 接入 Flutter / iOS 开发、验收与交付流程。复用宿主的模型、会话和工具,不启动另一套 coding agent。
4
+
5
+ ## 1. 安装 CLI
6
+
7
+ 需要 Node.js **22.18+**。npm 包名为 **`apphx`**,命令仍是 `app-factory`,Skill 名仍是 `apple-app-factory`。
8
+
9
+ | 包管理器 | 安装或升级 CLI | 运行命令 | 卸载 CLI |
10
+ | --- | --- | --- | --- |
11
+ | npm | `npm install -g apphx` | `app-factory` | `npm uninstall -g apphx` |
12
+ | pnpm | `pnpm add -g apphx` | `app-factory` | `pnpm remove -g apphx` |
13
+ | Yarn Classic 1.x | `yarn global add apphx` | `app-factory` | `yarn global remove apphx` |
14
+ | Yarn 2+ | 在目标项目中 `yarn add -D apphx` | `yarn exec app-factory` | `yarn remove apphx` |
15
+
16
+ 本地试用时将表格中的包名换成安装包的绝对路径,例如 `$PWD/apphx-1.0.0.tgz`;Yarn Classic 全局安装不能使用相对 tarball 路径。
17
+
18
+ pnpm 提示缺少全局 bin 目录时,先运行 `pnpm setup` 并重新打开终端。Yarn 2+ 没有 `yarn global`:后文的 `app-factory ...` 全部改用 `yarn exec app-factory ...`,并从安装了依赖的控制项目目录执行;宿主 Skill 也支持这个本地入口。
19
+
20
+ 安装过旧的本地预览包 `apple-app-factory` 时,先用原包管理器卸载那个 CLI 包,再安装 `apphx`,避免同名命令冲突。不要删除已有 Skill 或 `.factory`;随后用 `app-factory update <host>` 更新原安装。
21
+
22
+ ## 2. 接入你的 agent
23
+
24
+ 在要开发应用的项目目录执行对应命令,再重新打开该目录的 agent 会话。
25
+
26
+ | Agent | 安装工作流 | 在 agent 中启动 |
27
+ | --- | --- | --- |
28
+ | Codex App / CLI | `app-factory install codex` | `$apple-app-factory <需求>` |
29
+ | Claude Code | `app-factory install claude` | `/apple-app-factory <需求>` |
30
+ | Cursor | `app-factory install cursor` | `/apple-app-factory <需求>` |
31
+ | Pi | `app-factory install pi` | `/skill:apple-app-factory <需求>` |
32
+ | Gemini CLI | `app-factory install gemini` | `请启用 apple-app-factory skill,<需求>` |
33
+ | OpenCode | `app-factory install opencode` | `请加载 apple-app-factory skill,<需求>` |
34
+ | DeepSeek Harness | `app-factory install deepseek` | `请加载 apple-app-factory skill,<需求>` |
35
+ | Hermes | `app-factory install hermes --global` | `/apple-app-factory <需求>` |
36
+
37
+ 其他项目加 `--project /path/to/project`;用户级安装加 `--global`。Hermes 的全局目录跟随 `HERMES_HOME`,默认 `~/.hermes/skills`。也可在 Git 项目内使用 `app-factory install hermes`,然后由你执行 `hermes skills trust` 授予项目信任;安装器不会代为授权。见 [Hermes Skills](https://hermes-agent.nousresearch.com/docs/user-guide/features/skills)。
38
+
39
+ 例如在 Codex 中输入:
40
+
41
+ ```text
42
+ $apple-app-factory 开发行李配重应用:离线记录物品重量、检查超重、支持备份恢复;完成模拟器验收,只做本地构建,不上传。
43
+ ```
44
+
45
+ ## 3. 准备环境并确认接入
46
+
47
+ 实际 iOS 验收需要 **macOS、完整 Xcode、Flutter 和模拟器**。仅在允许本机执行构建与验收命令时运行:
48
+
49
+ ```sh
50
+ app-factory init --allow-local-execution
51
+ ```
52
+
53
+ 用 `flutter devices` 查询目标并设置真实的 `IOS_DEVICE_ID`;按 `factory.json` 配置验收命令,指定 SDK 时设置 `toolchain.flutterSdk`。然后检查:
54
+
55
+ ```sh
56
+ app-factory doctor --build-only
57
+ app-factory doctor --host codex
58
+ app-factory status
59
+ ```
60
+
61
+ 宿主 ID、`--project` / `--global` 与安装时保持一致。宿主检查只证明安装文件完整,实际加载看宿主的技能列表;Hermes 可用 `hermes skills list`。
62
+
63
+ 无需配置另一份模型 API key 或 builder/reviewer。agent 负责研究、开发和独立审查,CLI 执行验收与交付门禁。`status` 返回任务 ID、阶段、错误和产物路径;中断后继续同一任务 ID,不重复入队。
64
+
65
+ ## 4. 更新与卸载
66
+
67
+ 先按安装表升级 `apphx`(本地试用则安装新的 tarball),再显式更新宿主工作流:
68
+
69
+ ```sh
70
+ app-factory update codex
71
+ app-factory update hermes --global
72
+ ```
73
+
74
+ `install` 只安装;重复安装相同内容不做改动,已有不同版本会提示使用 `update`。`update` 不创建缺失的安装,也不会覆盖你修改过的文件。更新使用原来的 `--project` / `--global` 作用域。
75
+
76
+ 卸载时先移除工作流,再按安装表卸载 CLI:
77
+
78
+ ```sh
79
+ app-factory uninstall codex
80
+ app-factory uninstall hermes --global
81
+ ```
82
+
83
+ 不会删除应用代码或 `.factory` 任务数据。
84
+
85
+ ## 5. Hermes 与定时任务
86
+
87
+ 先在 Hermes 中完成一次正常接入,取得 `app-factory status` 中的任务 ID。定时任务**唤醒 Hermes 并续跑这个任务**,不是重复 `enqueue`,也不是只运行 `app-factory next`。
88
+
89
+ 将 `JOB_ID`、`/absolute/project` 换成真实值,使用相同的 Hermes profile:
90
+
91
+ ```sh
92
+ hermes cron create "every 30m" \
93
+ "继续 Factory 任务 JOB_ID,配置 /absolute/project/factory.json。先检查 status;若 running 且原执行是否结束不明,则报告并停止。否则读取 next,只处理一个返回 action;wait/done 或需要人工授权时停止。所有 Factory 操作使用 --build-only,不重复 enqueue,不上传。" \
94
+ --skill apple-app-factory \
95
+ --workdir /absolute/project \
96
+ --name app-factory-continue \
97
+ --paused
98
+ hermes cron list
99
+ ```
100
+
101
+ 核对后,将 `CRON_ID` 换成 Hermes 返回的定时任务 ID,再启用:
102
+
103
+ ```sh
104
+ hermes cron resume CRON_ID
105
+ hermes cron status
106
+ ```
107
+
108
+ - **Hermes gateway 必须运行**:可前台运行 `hermes gateway`,或按官方方式安装服务。`hermes cron tick` 不会替你启动 gateway。
109
+ - gateway 的执行环境必须能访问 CLI、同一配置/工作目录和 Mac 工具链;使用本地 Yarn 依赖时还需在控制项目中使用 `yarn exec app-factory`。
110
+ - 同一 Factory 任务只交给一个调度入口;不要同时手工编辑。Hermes 的定时任务去重不是 Factory 跨宿主的文件编辑锁。需要人工处理时先 `hermes cron pause CRON_ID`。
111
+ - 系统 cron / launchd / CI 也遵循相同边界:只查询状态可直接运行 CLI;需要模型工作时必须唤醒已有 agent 宿主,保留任务 ID、工作目录、环境和防重叠设置。
112
+
113
+ 创建为暂停状态不会自动执行。具体调度、恢复和服务安装见 [Hermes 定时任务文档](https://hermes-agent.nousresearch.com/docs/user-guide/features/cron)。
114
+
115
+ 默认只构建、不上传;`built` 不代表 App Store 批准。Skill 不接管宿主所有工具权限,CLI 只对本项目的任务推进和交付执行门禁。
116
+
117
+ <details>
118
+ <summary>开发、打包与版本发布(Turborepo + tsdown + repoctl)</summary>
119
+
120
+ 维护者使用 `.nvmrc` 指定的 **Node.js 24.22.1** 和 **pnpm 12.15.0**。根目录是私有工具 workspace,唯一发布包为 `packages/apple-app-factory` 中的 `apphx`。Turbo 编排构建、类型检查和测试,tsdown 生成发布产物,repoctl 管理版本和后续发布。
121
+
122
+ ```sh
123
+ pnpm install
124
+ pnpm exec repoctl config validate --json
125
+ pnpm test
126
+ pnpm run package:check
127
+ pnpm run package:pack
128
+ ```
129
+
130
+ 构建缓存包含 `dist`、README 与两份 MIT 许可证,并将根目录 README/LICENSE 纳入缓存输入;测试不缓存结果。只使用本地 Turbo 缓存,没有启用远程缓存。安装包输出到仓库根目录。
131
+
132
+ ### 首次发布 1.0.0
133
+
134
+ 首发使用维护者本机的 npm 登录,不依赖尚未配置的 Trusted Publisher。先完成上方检查,审查并提交实现和发布意图;原生版本准备要求干净工作树,不能在未提交改动上跳过检查:
135
+
136
+ ```sh
137
+ pnpm run release:plan
138
+ pnpm version -r --json
139
+ pnpm test
140
+ pnpm run package:check
141
+ pnpm run package:pack
142
+ ```
143
+
144
+ 确认原生版本结果为 `apphx@1.0.0`,再提交生成的 changelog、`.changeset/ledger.yaml` 等版本元数据,并将两次提交推送到 `main`。记录这个首发源码提交,再发布已验证的包:
145
+
146
+ ```sh
147
+ git rev-parse HEAD
148
+ npm login --registry=https://registry.npmjs.org
149
+ npm publish ./apphx-1.0.0.tgz --access public --registry=https://registry.npmjs.org
150
+ npm view apphx@1.0.0 version dist.integrity --json
151
+ ```
152
+
153
+ 保留首发提交 SHA 与 npm 返回的 `dist.integrity`。tarball 发布不能假定会自动附带当前仓库的 `gitHead`;后续恢复必须显式使用这个已推送的源码 SHA。
154
+
155
+ 首发后再在 npm 的 `apphx` 包设置中配置 Trusted Publisher:仓库 `daguanren21/apple-app-factory`、工作流 `release.yml`、environment `npm`;**Allowed actions 勾选 `npm publish`**,不能只保留默认的 `npm stage publish`。同时配置并保护 GitHub 的 `npm` environment。repoctl 原生上传要求公开源码仓库,以生成 provenance;CI 使用 OIDC,不保存长期 npm token。
156
+
157
+ 用 `oidc-audit` 检查可信发布身份。**下一次 `prepare` 前必须完成首发元数据恢复**:在 Release 工作流选择 `publish`,将 `source-sha` 设为上述首发提交,先 dry-run,再补齐 GitHub tag、Release 和 repoctl 检查点。已有 `apphx@1.0.0` 只核对、不重复上传;否则 repoctl 会因上一版尚未完整交付而拒绝准备下一版。
158
+
159
+ 新建 Trusted Publisher 后,须在 **2 天内**通过它完成一次新版本发布验证,否则配置过期;`oidc-audit` 或已发布版本的元数据恢复不算。临近下一次实际发布时再创建,过期则删除重建。见 [npm Trusted Publisher 配置规则](https://docs.npmjs.com/trusted-publishers/)。
160
+
161
+ ### 后续版本
162
+
163
+ ```sh
164
+ pnpm change apphx --bump patch --summary "描述本次用户可见改动"
165
+ pnpm run release:plan
166
+ ```
167
+
168
+ 提交改动后,在 GitHub Actions 的 **Release** 中运行 `plan`、`prepare`,审查并合并版本 PR,然后运行 `publish`(先勾选 `dry-run` 预览)。`prepare` 会创建并推送版本 PR,不是本地-only 命令。
169
+
170
+ 使用默认 `GITHUB_TOKEN` 准备 PR 时,需要允许 Actions 创建 PR,并在生成的版本分支手动运行 **Check**;该 token 创建的 PR 不会自动触发普通 PR 工作流。已有 GitHub App 时,可配置 `REPOCTL_APP_CLIENT_ID` 变量和 `REPOCTL_APP_PRIVATE_KEY` secret。
171
+
172
+ Release 仅手动触发,不会因普通 push 自动上传。保留 `repoctl-release-state` 检查点;恢复细节见 [repoctl 发布文档](https://repo.icebreaker.top/tasks/release)。不要对私有根包直接执行 `npm publish`。
173
+
174
+ 本项目不安装本地提交钩子,Release 是自有的手动工作流;`repo doctor` 对这两项的提示会保留,不应为清除提示而自动覆盖发布流程。
175
+
176
+ 0.1 的 provider 配置与后台 `run` / `service` 入口不再使用;配置按 [factory.json](https://github.com/daguanren21/apple-app-factory/blob/main/packages/apple-app-factory/examples/factory.json) 更新,不要删除 `.factory`。全部命令见 `app-factory --help`;需求示例见 [luggage.json](https://github.com/daguanren21/apple-app-factory/blob/main/packages/apple-app-factory/examples/luggage.json)。
177
+
178
+ </details>
179
+
180
+ ## License
181
+
182
+ [MIT](LICENSE)
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+ // src/apple-types.ts
3
+ const text = z.string().min(1);
4
+ const effect = z.strictObject({ id: text.optional() });
5
+ const AppleReceiptSchema = z.strictObject({
6
+ identity: z.strictObject({
7
+ appId: text,
8
+ bundleId: text,
9
+ version: text,
10
+ buildNumber: text
11
+ }).optional(),
12
+ pending: z.strictObject({
13
+ key: text,
14
+ path: text.optional(),
15
+ method: z.enum([
16
+ "POST",
17
+ "PATCH",
18
+ "PUT"
19
+ ]).optional(),
20
+ startedAt: z.iso.datetime().optional()
21
+ }).nullable().optional(),
22
+ effects: z.record(z.string(), effect).optional(),
23
+ lastEffect: z.strictObject({
24
+ key: text,
25
+ id: text.optional(),
26
+ reconciled: z.boolean().optional(),
27
+ completedAt: z.iso.datetime().optional()
28
+ }).optional(),
29
+ upload: z.strictObject({
30
+ status: z.enum([
31
+ "intent",
32
+ "delivered",
33
+ "reconciled"
34
+ ]),
35
+ startedAt: z.iso.datetime().optional()
36
+ }).optional(),
37
+ phase: z.enum(["processing", "submitted"]).optional(),
38
+ versionId: text.optional(),
39
+ buildId: text.optional(),
40
+ submissionId: text.optional(),
41
+ assetParts: z.record(z.string(), z.number().int().nonnegative().safe()).optional()
42
+ });
43
+ export { AppleReceiptSchema };
44
+
45
+ //# sourceMappingURL=apple-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-types.js","names":[],"sources":["../src/apple-types.ts"],"sourcesContent":["import { z } from 'zod';\n\nconst text = z.string().min(1);\nconst effect = z.strictObject({ id: text.optional() });\n\nexport const AppleReceiptSchema = z.strictObject({\n identity: z.strictObject({ appId: text, bundleId: text, version: text, buildNumber: text }).optional(),\n pending: z.strictObject({ key: text, path: text.optional(), method: z.enum(['POST', 'PATCH', 'PUT']).optional(), startedAt: z.iso.datetime().optional() }).nullable().optional(),\n effects: z.record(z.string(), effect).optional(),\n lastEffect: z.strictObject({ key: text, id: text.optional(), reconciled: z.boolean().optional(), completedAt: z.iso.datetime().optional() }).optional(),\n upload: z.strictObject({ status: z.enum(['intent', 'delivered', 'reconciled']), startedAt: z.iso.datetime().optional() }).optional(),\n phase: z.enum(['processing', 'submitted']).optional(),\n versionId: text.optional(),\n buildId: text.optional(),\n submissionId: text.optional(),\n assetParts: z.record(z.string(), z.number().int().nonnegative().safe()).optional(),\n});\n\nexport interface AppleReceipt extends z.infer<typeof AppleReceiptSchema> {}\n\nexport interface ApplePollResult {\n status: 'waiting' | 'approved' | 'rejected';\n detail: string;\n distributionReady?: boolean;\n available?: boolean;\n}\n"],"mappings":";;AAEA,MAAM,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AAC7B,MAAM,SAAS,EAAE,aAAa,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;AAErD,MAAa,qBAAqB,EAAE,aAAa;CAC/C,UAAU,EAAE,aAAa;EAAE,OAAO;EAAM,UAAU;EAAM,SAAS;EAAM,aAAa;CAAK,CAAC,CAAC,CAAC,SAAS;CACrG,SAAS,EAAE,aAAa;EAAE,KAAK;EAAM,MAAM,KAAK,SAAS;EAAG,QAAQ,EAAE,KAAK;GAAC;GAAQ;GAAS;EAAK,CAAC,CAAC,CAAC,SAAS;EAAG,WAAW,EAAE,IAAI,SAAS,CAAC,CAAC,SAAS;CAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAC/K,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,SAAS;CAC/C,YAAY,EAAE,aAAa;EAAE,KAAK;EAAM,IAAI,KAAK,SAAS;EAAG,YAAY,EAAE,QAAQ,CAAC,CAAC,SAAS;EAAG,aAAa,EAAE,IAAI,SAAS,CAAC,CAAC,SAAS;CAAE,CAAC,CAAC,CAAC,SAAS;CACtJ,QAAQ,EAAE,aAAa;EAAE,QAAQ,EAAE,KAAK;GAAC;GAAU;GAAa;EAAY,CAAC;EAAG,WAAW,EAAE,IAAI,SAAS,CAAC,CAAC,SAAS;CAAE,CAAC,CAAC,CAAC,SAAS;CACnI,OAAO,EAAE,KAAK,CAAC,cAAc,WAAW,CAAC,CAAC,CAAC,SAAS;CACpD,WAAW,KAAK,SAAS;CACzB,SAAS,KAAK,SAAS;CACvB,cAAc,KAAK,SAAS;CAC5B,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;AACnF,CAAC"}