akari-video 1.5.0 → 1.5.2-beta.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.
Files changed (252) hide show
  1. package/README.md +2 -2
  2. package/bin/akari.mjs +4 -0
  3. package/package.json +1 -1
  4. package/src/cli.mjs +6 -0
  5. package/src/connect/connectors.json +1 -1
  6. package/src/lint-command.mjs +85 -0
  7. package/src/messages.mjs +1 -1
  8. package/src/narration-command.mjs +11 -7
  9. package/src/new-command.mjs +12 -6
  10. package/src/playbook-command.mjs +24 -14
  11. package/src/preview-command.mjs +281 -0
  12. package/src/repo-assets.mjs +24 -7
  13. package/src/status-core/status.mjs +2 -2
  14. package/src/store-command.mjs +1 -1
  15. package/src/template-command.mjs +21 -0
  16. package/src/tts-engines.mjs +39 -18
  17. package/vendor/.akari-capability-sources.json +34 -32
  18. package/vendor/catalog/INDEX.md +13 -3
  19. package/vendor/catalog/audio/candidates-legacy.json +4 -4
  20. package/vendor/catalog/audio/candidates.html +1 -1
  21. package/vendor/catalog/audio/candidates.json +2 -2
  22. package/vendor/docs/contract-2026-10-16-vocabulary-v0.md +5 -3
  23. package/vendor/packages/akari-launcher/README.md +2 -2
  24. package/vendor/packages/akari-launcher/package.json +1 -1
  25. package/vendor/packages/akari-tools/bin/speakers.mjs +0 -0
  26. package/vendor/packages/audio-library-setup/README.md +1 -1
  27. package/vendor/packages/audio-library-setup/bin/fetch-akari-sounds.mjs +1 -1
  28. package/vendor/packages/audio-library-setup/bin/generate-candidates-html.mjs +1 -1
  29. package/vendor/packages/audio-library-setup/shared/akari-sounds.mjs +1 -1
  30. package/vendor/packages/audio-library-setup/shared/candidates.mjs +4 -4
  31. package/vendor/packages/creator-root/src/index.mjs +1 -1
  32. package/vendor/packages/creator-root/test/direct-tts-providers.test.mjs +3 -0
  33. package/vendor/packages/edit-lint/README.md +19 -1
  34. package/vendor/packages/edit-lint/src/edit-lint.mjs +143 -20
  35. package/vendor/packages/edit-lint/src/fix.mjs +252 -0
  36. package/vendor/packages/edit-lint/src/i18n.mjs +4 -0
  37. package/vendor/packages/edit-lint/src/lint/box-sizing-messages.json +4 -0
  38. package/vendor/packages/edit-lint/src/lint/caption-fonts.mjs +139 -0
  39. package/vendor/packages/edit-lint/src/lint/captions.mjs +5 -3
  40. package/vendor/packages/edit-lint/src/lint/cuts-tracks.mjs +76 -1
  41. package/vendor/packages/edit-lint/src/lint/fragment-box-sizing.mjs +385 -0
  42. package/vendor/packages/edit-lint/src/lint/fragment-css-collision.mjs +73 -0
  43. package/vendor/packages/edit-lint/src/lint/fragment-css.mjs +74 -0
  44. package/vendor/packages/edit-lint/src/lint/overlays.mjs +147 -7
  45. package/vendor/packages/edit-lint/src/lint/review.mjs +1 -1
  46. package/vendor/packages/edit-store/lib/internal-model.d.ts +15 -8
  47. package/vendor/packages/edit-store/lib/internal-model.js +94 -28
  48. package/vendor/packages/edit-store/lib/webview-kernel.js +24 -7
  49. package/vendor/packages/i18n/locales/en/annotations.json +11 -11
  50. package/vendor/packages/i18n/locales/en/annotationscommon.json +1 -1
  51. package/vendor/packages/i18n/locales/en/cli.json +50 -17
  52. package/vendor/packages/i18n/locales/en/connect.json +1 -1
  53. package/vendor/packages/i18n/locales/en/inspector.json +12 -12
  54. package/vendor/packages/i18n/locales/en/lint-messages.json +15 -1
  55. package/vendor/packages/i18n/locales/en/lint.json +1 -1
  56. package/vendor/packages/i18n/locales/en/preview.json +1 -1
  57. package/vendor/packages/i18n/locales/en/project.json +25 -16
  58. package/vendor/packages/i18n/locales/en/report-cards.json +1 -1
  59. package/vendor/packages/i18n/locales/en/report-project.json +2 -2
  60. package/vendor/packages/i18n/locales/en/shell.json +3 -3
  61. package/vendor/packages/i18n/locales/en/skills.json +26 -24
  62. package/vendor/packages/i18n/locales/en/surfaces.json +30 -30
  63. package/vendor/packages/i18n/locales/en/tools.json +13 -1
  64. package/vendor/packages/i18n/locales/en/transcript.json +8 -8
  65. package/vendor/packages/i18n/locales/ja/annotations.json +14 -14
  66. package/vendor/packages/i18n/locales/ja/annotationscommon.json +1 -1
  67. package/vendor/packages/i18n/locales/ja/cli.json +52 -19
  68. package/vendor/packages/i18n/locales/ja/connect.json +1 -1
  69. package/vendor/packages/i18n/locales/ja/inspector.json +13 -13
  70. package/vendor/packages/i18n/locales/ja/lint-messages.json +15 -1
  71. package/vendor/packages/i18n/locales/ja/lint.json +1 -1
  72. package/vendor/packages/i18n/locales/ja/preview.json +1 -1
  73. package/vendor/packages/i18n/locales/ja/project.json +27 -18
  74. package/vendor/packages/i18n/locales/ja/report-cards.json +1 -1
  75. package/vendor/packages/i18n/locales/ja/report-project.json +2 -2
  76. package/vendor/packages/i18n/locales/ja/shell.json +4 -4
  77. package/vendor/packages/i18n/locales/ja/skills.json +26 -24
  78. package/vendor/packages/i18n/locales/ja/surfaces.json +30 -30
  79. package/vendor/packages/i18n/locales/ja/tools.json +13 -1
  80. package/vendor/packages/i18n/locales/ja/transcript.json +8 -8
  81. package/vendor/packages/overlay-runtime/README.md +44 -15
  82. package/vendor/packages/overlay-runtime/src/three-runtime.js +671 -40
  83. package/vendor/packages/overlay-runtime/src/vendor/three-bundle.js +978 -289
  84. package/vendor/packages/project-scaffold/src/index.mjs +34 -8
  85. package/vendor/packages/schemas/ai-models.json +19 -0
  86. package/vendor/packages/schemas/ai-models.schema.json +1 -0
  87. package/vendor/packages/schemas/examples/kit-manifest-v1-name-collision/manifest.json +1 -1
  88. package/vendor/packages/schemas/examples/kit-manifest-v1-name-collision/skills/{edit-lint → verify-edit}/SKILL.md +1 -1
  89. package/vendor/packages/schemas/review.schema.json +2 -2
  90. package/vendor/packages/schemas/skill-renames.mjs +393 -0
  91. package/vendor/packages/schemas/skill-renames.schema.json +43 -0
  92. package/vendor/packages/schemas/test/ai-models.test.mjs +20 -2
  93. package/vendor/packages/schemas/test/kit-manifest.test.mjs +1 -1
  94. package/vendor/packages/schemas/test/skill-renames.test.mjs +117 -0
  95. package/vendor/packages/schemas/test/validate-captions.test.mjs +1 -1
  96. package/vendor/packages/schemas/test/validate-review.test.mjs +2 -2
  97. package/vendor/presets/luts/INDEX.md +1 -1
  98. package/vendor/skills/akari/SKILL.md +1 -1
  99. package/vendor/skills/analyze-footage/bin/person-matte/resolve-packages.mjs +1 -1
  100. package/vendor/skills/author-scene3d/SKILL.md +45 -0
  101. package/vendor/skills/{bake-3d/SKILL.md → author-scene3d/bake.md} +11 -25
  102. package/vendor/skills/{overlay-authoring → author-scene3d}/device-materials.md +3 -3
  103. package/vendor/skills/author-scene3d/live.md +575 -0
  104. package/vendor/skills/create-project/SKILL.md +10 -6
  105. package/vendor/skills/declare-audio/SKILL.md +2 -2
  106. package/vendor/skills/{address-review → edit-from-review}/SKILL.md +6 -6
  107. package/vendor/skills/{address-review → edit-from-review}/bin/list.mjs +2 -2
  108. package/vendor/skills/{address-review → edit-from-review}/bin/respond.mjs +1 -1
  109. package/vendor/skills/edit-plan/SKILL.md +1 -1
  110. package/vendor/skills/edit-plan/autonomy.md +2 -2
  111. package/vendor/skills/edit-plan/avatar-resolution.md +1 -1
  112. package/vendor/skills/edit-plan/beat-sync.md +3 -3
  113. package/vendor/skills/edit-plan/execution.md +1 -1
  114. package/vendor/skills/edit-plan/expression-selection.md +3 -3
  115. package/vendor/skills/edit-plan/plan-json.md +1 -1
  116. package/vendor/skills/edit-plan/workflow.md +1 -1
  117. package/vendor/skills/{beat-sync-edit → edit-to-beat}/SKILL.md +2 -2
  118. package/vendor/skills/{beat-sync-edit → edit-to-beat}/pitfalls.md +1 -1
  119. package/vendor/skills/generate-image/SKILL.md +42 -0
  120. package/vendor/skills/generate-image/image.md +38 -0
  121. package/vendor/skills/generate-narration/SKILL.md +4 -4
  122. package/vendor/skills/generate-narration/engines.md +79 -63
  123. package/vendor/skills/generate-narration/lang/en.md +8 -6
  124. package/vendor/skills/generate-narration/lang/ja.md +17 -11
  125. package/vendor/skills/generate-narration/voice-profile-setup.md +3 -3
  126. package/vendor/skills/{generate-media → generate-video}/SKILL.md +8 -6
  127. package/vendor/skills/{generate-media → generate-video}/chain.md +1 -1
  128. package/vendor/skills/generate-video/engines.md +23 -0
  129. package/vendor/skills/generate-video/still.md +65 -0
  130. package/vendor/skills/overlay-authoring/3d.md +3 -575
  131. package/vendor/skills/overlay-authoring/SKILL.md +3 -3
  132. package/vendor/skills/overlay-authoring/dev-fixtures/test_device_materials.py +1 -1
  133. package/vendor/skills/overlay-authoring/telop.md +1 -1
  134. package/vendor/skills/renames.json +16 -0
  135. package/vendor/skills/render-cut/SKILL.md +2 -2
  136. package/vendor/skills/research-plan/SKILL.md +1 -1
  137. package/vendor/skills/research-plan/competitor.md +2 -2
  138. package/vendor/skills/research-plan/lang/ja.md +1 -1
  139. package/vendor/skills/research-plan/trends.md +1 -1
  140. package/vendor/skills/{critique-cut → review-cut}/SKILL.md +6 -6
  141. package/vendor/skills/{critique-cut → review-cut}/bin/index.js +1 -1
  142. package/vendor/skills/{critique-cut → review-cut}/workflow.md +3 -3
  143. package/vendor/skills/{compile-review-session → review-from-recording}/SKILL.md +5 -5
  144. package/vendor/skills/{compile-review-session → review-from-recording}/bin/compile-review-session.mjs +1 -1
  145. package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/install-root.mjs +2 -2
  146. package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/README.md +4 -4
  147. package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-69-review-json/README.md +6 -6
  148. package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-69-review-json/reproduce.mjs +1 -1
  149. package/vendor/skills/review-from-recording/dev-fixtures/issue-79/README.md +3 -0
  150. package/vendor/skills/{compile-review-session → review-from-recording}/test/helpers/issue-79-fixture.mjs +1 -1
  151. package/vendor/skills/{compile-review-session → review-from-recording}/test/issue-79-snapshot.test.mjs +1 -1
  152. package/vendor/skills/setup-chat-approval/SKILL.md +3 -3
  153. package/vendor/skills/setup-chat-approval/bin/resolve-packages.mjs +1 -1
  154. package/vendor/skills/setup-chat-approval/setup-token.md +1 -1
  155. package/vendor/skills/{manage-connections → setup-connections}/SKILL.md +53 -7
  156. package/vendor/skills/{manage-connections → setup-connections}/bin/doctor.mjs +1 -1
  157. package/vendor/skills/{manage-connections → setup-connections}/bin/resolve-packages.mjs +1 -1
  158. package/vendor/skills/setup-connections/dev-fixtures/resolve-packages-newest/README.md +17 -0
  159. package/vendor/skills/{manage-connections → setup-connections}/dev-fixtures/resolve-packages-newest/reproduce.mjs +2 -2
  160. package/vendor/skills/{manage-connections → setup-connections}/test/resolve-packages-newest.test.mjs +1 -1
  161. package/vendor/skills/setup-library/SKILL.md +82 -1
  162. package/vendor/skills/{setup-audio-library/assisted-fetch.md → setup-library/audio-assisted-fetch.md} +2 -2
  163. package/vendor/skills/{setup-audio-library/candidate-list.md → setup-library/audio-candidate-list.md} +1 -1
  164. package/vendor/skills/{setup-audio-library/drop-folder.md → setup-library/audio-drop-folder.md} +3 -3
  165. package/vendor/skills/{setup-audio-library/gallery.md → setup-library/audio-gallery.md} +1 -1
  166. package/vendor/skills/setup-library/tools-check.md +1 -1
  167. package/vendor/skills/{edit-lint → verify-edit}/SKILL.md +2 -2
  168. package/vendor/skills/{verify → verify-project}/SKILL.md +2 -2
  169. package/vendor/templates/project-default/.claude/skills/README.md +4 -3
  170. package/vendor/templates/project-default/AGENTS.md +5 -4
  171. package/vendor/templates/project-default/CLAUDE.md +5 -4
  172. package/vendor/skills/compile-review-session/dev-fixtures/issue-79/README.md +0 -3
  173. package/vendor/skills/connect/SKILL.md +0 -24
  174. package/vendor/skills/generate-media/engines.md +0 -25
  175. package/vendor/skills/generate-media/still.md +0 -75
  176. package/vendor/skills/manage-connections/dev-fixtures/resolve-packages-newest/README.md +0 -17
  177. package/vendor/skills/setup-audio-library/SKILL.md +0 -96
  178. /package/vendor/skills/{overlay-authoring → author-scene3d}/scripts/device_materials.py +0 -0
  179. /package/vendor/skills/{address-review → edit-from-review}/bin/core/review-store.mjs +0 -0
  180. /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/captions.json +0 -0
  181. /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/edit.json +0 -0
  182. /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/review.json +0 -0
  183. /package/vendor/skills/{address-review → edit-from-review}/test/review-loss.test.mjs +0 -0
  184. /package/vendor/skills/{address-review → edit-from-review}/test/review-store.test.mjs +0 -0
  185. /package/vendor/skills/{beat-sync-edit → edit-to-beat}/generator.md +0 -0
  186. /package/vendor/skills/{beat-sync-edit → edit-to-beat}/templates/gen-timeline.mjs +0 -0
  187. /package/vendor/skills/{beat-sync-edit → edit-to-beat}/verify-loop.md +0 -0
  188. /package/vendor/skills/{generate-media → generate-video}/video.md +0 -0
  189. /package/vendor/skills/{critique-cut → review-cut}/bin/used-ranges.mjs +0 -0
  190. /package/vendor/skills/{critique-cut → review-cut}/bin/used-ranges.test.mjs +0 -0
  191. /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/canvas-compiler.mjs +0 -0
  192. /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/compiler.mjs +0 -0
  193. /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/review-store.mjs +0 -0
  194. /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/time-mapping.mjs +0 -0
  195. /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/transcription.mjs +0 -0
  196. /package/vendor/skills/{compile-review-session → review-from-recording}/compilation-rules.md +0 -0
  197. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/audio.wav +0 -0
  198. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/edit.snapshot.json +0 -0
  199. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/events.jsonl +0 -0
  200. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/session.json +0 -0
  201. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/audio.wav +0 -0
  202. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/edit.snapshot.json +0 -0
  203. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/events.jsonl +0 -0
  204. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/session.json +0 -0
  205. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/audio.wav +0 -0
  206. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/edit.snapshot.json +0 -0
  207. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/events.jsonl +0 -0
  208. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/session.json +0 -0
  209. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/audio.wav +0 -0
  210. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/edit.snapshot.json +0 -0
  211. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/events.jsonl +0 -0
  212. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/session.json +0 -0
  213. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0005/session.json +0 -0
  214. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/audio.wav +0 -0
  215. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/events.jsonl +0 -0
  216. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/session.json +0 -0
  217. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/audio.wav +0 -0
  218. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/edit.snapshot.json +0 -0
  219. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/events.jsonl +0 -0
  220. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/session.json +0 -0
  221. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/audio.wav +0 -0
  222. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/edit.snapshot.json +0 -0
  223. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/events.jsonl +0 -0
  224. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/session.json +0 -0
  225. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/transcript.json +0 -0
  226. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/audio.wav +0 -0
  227. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/edit.snapshot.json +0 -0
  228. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/events.jsonl +0 -0
  229. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/session.json +0 -0
  230. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/audio.wav +0 -0
  231. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/edit.snapshot.json +0 -0
  232. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/events.jsonl +0 -0
  233. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/session.json +0 -0
  234. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review.json +0 -0
  235. /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-79/reproduce.mjs +0 -0
  236. /package/vendor/skills/{compile-review-session → review-from-recording}/test/canvas-compiler.test.mjs +0 -0
  237. /package/vendor/skills/{compile-review-session → review-from-recording}/test/compiler.test.mjs +0 -0
  238. /package/vendor/skills/{compile-review-session → review-from-recording}/test/install-root.test.mjs +0 -0
  239. /package/vendor/skills/{compile-review-session → review-from-recording}/test/review-loss.test.mjs +0 -0
  240. /package/vendor/skills/{compile-review-session → review-from-recording}/test/ui-events.test.mjs +0 -0
  241. /package/vendor/skills/{manage-connections → setup-connections}/bin/resolve-connections.mjs +0 -0
  242. /package/vendor/skills/{manage-connections → setup-connections}/bin/test/doctor-i18n.test.mjs +0 -0
  243. /package/vendor/skills/{manage-connections → setup-connections}/bin/test/resolve-packages.test.mjs +0 -0
  244. /package/vendor/skills/{manage-connections → setup-connections}/test/connectors-doctor.test.mjs +0 -0
  245. /package/vendor/skills/{manage-connections → setup-connections}/test/direct-tts-doctor.test.mjs +0 -0
  246. /package/vendor/skills/{manage-connections → setup-connections}/test/doctor-key-source.test.mjs +0 -0
  247. /package/vendor/skills/{manage-connections → setup-connections}/test/fieldreport-report-location.test.mjs +0 -0
  248. /package/vendor/skills/{manage-connections → setup-connections}/test/resolve-connections-defaults.test.mjs +0 -0
  249. /package/vendor/skills/{manage-connections → setup-connections}/test/resolve-connections.test.mjs +0 -0
  250. /package/vendor/skills/{setup-audio-library/first-party.md → setup-library/audio-first-party.md} +0 -0
  251. /package/vendor/skills/{setup-audio-library/store-unlock.md → setup-library/audio-store-unlock.md} +0 -0
  252. /package/vendor/skills/{edit-lint → verify-edit}/preview.md +0 -0
package/README.md CHANGED
@@ -21,7 +21,7 @@ akari
21
21
  │ (.akari/intake.json は status: "draft" で生成される)
22
22
  │
23
23
  ├─ 3. 接続状態を確認する:
24
- │ skills/manage-connections/bin/doctor.mjs を再利用して
24
+ │ skills/setup-connections/bin/doctor.mjs を再利用して
25
25
  │ .akari/connections.json の doctor ブロックを更新・表示する
26
26
  │ (キーの値は一切表示しない)
27
27
  │
@@ -47,7 +47,7 @@ DL・sha256 検証・適用まで実行。それ以外(npm グローバル / g
47
47
  `akari init`(作業場の作成・確認のみ)/
48
48
  `akari new <target-dir> [--template <path>]`(新規プロジェクト作成)/
49
49
  `akari narration generate ...`(VOICEVOX / fal-qwen3 ナレーション生成)/
50
- `akari internal beat-sync-<beatmap|probe-frame|render-when-idle> ...`(beat-sync-edit 内部実行物)/
50
+ `akari internal beat-sync-<beatmap|probe-frame|render-when-idle> ...`(edit-to-beat 内部実行物)/
51
51
  `akari sounds [--variant wav] [--force]`(公式音源の一括ダウンロード。プロンプトなし・headless 可)/
52
52
  `akari store <connect|status|install|download|disconnect>`(AKARI Store 連携。マイページで発行した
53
53
  接続トークンを `~/.akari/store-credentials.json`(0600)に保存し、購入済み一覧の確認と
package/bin/akari.mjs CHANGED
@@ -17,6 +17,8 @@ import { runDoctorCommand } from '../src/doctor-command.mjs';
17
17
  import { runGenerateCommand } from '../src/generate-command.mjs';
18
18
  import { runStoryboardCommand } from '../src/storyboard-command.mjs';
19
19
  import { runWorldCommand } from '../src/world-command.mjs';
20
+ import { runLintCommand } from '../src/lint-command.mjs';
21
+ import { runPreviewCommand } from '../src/preview-command.mjs';
20
22
  import { runSkillsCommand, refreshEntrySkillOnLaunch } from '../src/skills-command.mjs';
21
23
  import { resolveRuntimePaths } from '../src/runtime-diagnostics.mjs';
22
24
  import { maybeApplyPendingUpdateOnLaunch, resolveInstalledVersionInfo } from '../src/update-check.mjs';
@@ -77,6 +79,8 @@ const commands = {
77
79
  generate: runGenerateCommand,
78
80
  storyboard: runStoryboardCommand,
79
81
  world: runWorldCommand,
82
+ lint: runLintCommand,
83
+ preview: runPreviewCommand,
80
84
  };
81
85
 
82
86
  // トップレベルのフラグも、振り分けと候補判定に同じ表を使う。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akari-video",
3
- "version": "1.5.0",
3
+ "version": "1.5.2-beta.1",
4
4
  "description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -32,7 +32,10 @@ import { runWordBookCommand } from './word-book-command.mjs';
32
32
  import { runConnectCommand } from './connect-command.mjs';
33
33
  import { runProjectCommand } from './project-command.mjs';
34
34
  import { runPlaybookCommand } from './playbook-command.mjs';
35
+ import { runTemplateCommand } from './template-command.mjs';
35
36
  import { runRecipeCommand } from './recipe-command.mjs';
37
+ import { runLintCommand } from './lint-command.mjs';
38
+ import { runPreviewCommand } from './preview-command.mjs';
36
39
  import { resolveRuntimePaths } from './runtime-diagnostics.mjs';
37
40
  import { cliText } from './i18n.mjs';
38
41
 
@@ -48,7 +51,10 @@ export const runSubcommands = {
48
51
  connect: runConnectCommand,
49
52
  project: runProjectCommand,
50
53
  playbook: runPlaybookCommand,
54
+ template: runTemplateCommand,
51
55
  recipe: runRecipeCommand,
56
+ lint: runLintCommand,
57
+ preview: runPreviewCommand,
52
58
  };
53
59
 
54
60
  /**
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "id": "3d-recipes",
13
13
  "group": "tools",
14
- "name": "3D レシピ",
14
+ "name": "3D の設定",
15
15
  "description": "Blender で 3D 素材を作る",
16
16
  "icon": "3D",
17
17
  "kind": "blender-recipe",
@@ -0,0 +1,85 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+
4
+ import { cliText } from './i18n.mjs';
5
+ import { resolveRuntimePaths } from './runtime-diagnostics.mjs';
6
+
7
+ // 値を 1 つ取るオプション。プロジェクトの位置引数と取り違えないよう読み飛ばす。
8
+ const VALUE_FLAGS = new Set([
9
+ '--engine',
10
+ '--silence-error-seconds',
11
+ '--max-volume-error-db',
12
+ '--caption-silence-warn-percent',
13
+ '--declarations',
14
+ '--ffprobe'
15
+ ]);
16
+
17
+ export function lintHelp() {
18
+ return [
19
+ cliText('cli.lint.help.usage'),
20
+ '',
21
+ cliText('cli.lint.help.description'),
22
+ cliText('cli.lint.help.project'),
23
+ cliText('cli.lint.help.fix'),
24
+ cliText('cli.lint.help.media'),
25
+ cliText('cli.lint.help.json'),
26
+ cliText('cli.lint.help.engine'),
27
+ cliText('cli.lint.help.help')
28
+ ];
29
+ }
30
+
31
+ function hasProjectArgument(argv) {
32
+ for (let index = 0; index < argv.length; index += 1) {
33
+ const argument = argv[index];
34
+ if (argument === '--') return index + 1 < argv.length;
35
+ if (VALUE_FLAGS.has(argument)) {
36
+ index += 1;
37
+ continue;
38
+ }
39
+ if (argument.startsWith('-')) continue;
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+
45
+ /**
46
+ * `akari lint [<project>] [--fix] ...` — 決定論的な edit-lint CLI をそのまま呼ぶ。
47
+ * 既定では `--no-reports` を付けて一切書き込まない。`--fix` のときだけ edit-lint の
48
+ * 修正経路(退避・差分・再検査)を通す。
49
+ */
50
+ export async function runLintCommand(argv, options = {}) {
51
+ const log = options.log ?? ((line) => console.log(line));
52
+ const error = options.error ?? ((line) => console.error(line));
53
+
54
+ if (argv.includes('--help') || argv.includes('-h')) {
55
+ for (const line of lintHelp()) log(line);
56
+ return { exitCode: 0 };
57
+ }
58
+
59
+ const runtime = options.runtime ?? resolveRuntimePaths({ env: options.env ?? process.env });
60
+ const editLintPath = options.editLintPath ?? runtime.edit_lint?.path ?? null;
61
+ if (!editLintPath || !existsSync(editLintPath)) {
62
+ error(cliText('cli.lint.error.scriptMissing'));
63
+ return { exitCode: 2 };
64
+ }
65
+
66
+ const forwarded = [...argv];
67
+ if (!hasProjectArgument(forwarded)) forwarded.push('.');
68
+ if (!forwarded.includes('--fix')) forwarded.push('--no-reports');
69
+
70
+ const env = { ...(options.env ?? process.env) };
71
+ const electron = options.electronRuntime ?? Boolean(process.versions.electron);
72
+ if (electron && env.ELECTRON_RUN_AS_NODE !== '1') env.ELECTRON_RUN_AS_NODE = '1';
73
+
74
+ const spawn = options.spawn ?? spawnSync;
75
+ const result = spawn(options.execPath ?? process.execPath, [editLintPath, ...forwarded], {
76
+ stdio: options.stdio ?? 'inherit',
77
+ cwd: options.cwd ?? process.cwd(),
78
+ env
79
+ });
80
+ return {
81
+ exitCode: typeof result?.status === 'number' ? result.status : 2,
82
+ stdout: result?.stdout,
83
+ stderr: result?.stderr
84
+ };
85
+ }
package/src/messages.mjs CHANGED
@@ -335,7 +335,7 @@ export function describeCliHelp() {
335
335
  cliText('cli.help.header.usage'),
336
336
  '',
337
337
  cliText('cli.help.commands.heading'),
338
- ...['launch', 'store', 'sounds', 'doctor', 'speakers', 'toolsVoice', 'update', 'status', 'migrate', 'migrateLook', 'generate', 'storyboard', 'project', 'playbook', 'recipe', 'clean'].map((name) => cliText(`cli.help.commands.${name}`)),
338
+ ...['launch', 'preview', 'store', 'sounds', 'doctor', 'speakers', 'toolsVoice', 'update', 'status', 'migrate', 'migrateLook', 'generate', 'storyboard', 'project', 'playbook', 'recipe', 'lint', 'clean'].map((name) => cliText(`cli.help.commands.${name}`)),
339
339
  '',
340
340
  cliText('cli.help.developer.heading'),
341
341
  ...['opencode', 'claude', 'yes', 'version', 'new', 'init', 'other', 'help'].map((name) => cliText(`cli.help.developer.${name}`)),
@@ -59,7 +59,7 @@ async function projectLanguage(projectRoot, cliLang) {
59
59
  const usage = (language) => [
60
60
  cliText('cli.narration.help.usage', {}, language),
61
61
  " akari narration generate \\",
62
- " --project <projectDir> [--engine <voicevox|gemini-tts|irodori|fal-qwen3|gemini-3.1-flash-tts|elevenlabs-v3|minimax-2.6-hd|chatterbox|index-tts-2|fish-s2.1-pro|gemini-3.8-flash-tts>] \\",
62
+ " --project <projectDir> [--engine <voicevox|gemini-tts|irodori|fal-qwen3|gemini-3.1-flash-tts|elevenlabs-v3|minimax-2.6-hd|chatterbox|index-tts-2|fish-s2.1-pro|fish-drama-3|gemini-3.8-flash-tts>] \\",
63
63
  cliText('cli.narration.help.reading', {}, language),
64
64
  cliText('cli.narration.help.timeline', {}, language),
65
65
  " [--lang <code>] [--speaker 3] [--voice Leda] [--style <text>] [--speed 1] \\",
@@ -322,10 +322,10 @@ async function parseArguments(argv) {
322
322
  irodoriTimeout();
323
323
  } else {
324
324
  const engine = ttsEngine(options.engine);
325
- options.voice ??= engine ? (engine.id === 'fish-s2.1-pro' && options.language !== 'ja' ? null
325
+ options.voice ??= engine ? (engine.provider === 'fish-audio' && options.language !== 'ja' ? null
326
326
  : engine.default_voices && Object.hasOwn(engine.default_voices, options.language)
327
327
  ? engine.default_voices[options.language] : engine.default_voice) : 'Leda';
328
- if (options.engine === 'fish-s2.1-pro' && !options.voice && !options.profile) {
328
+ if (engine?.provider === 'fish-audio' && !options.voice && !options.profile) {
329
329
  throw new PublicError(cliText('cli.narration.error.fishDefaultVoiceMissing'), 2);
330
330
  }
331
331
  }
@@ -773,7 +773,7 @@ function applyToEditJson(projectDir, entry, io) {
773
773
  // --- dry-run ---
774
774
 
775
775
  function maskKey(secret) {
776
- // 実際のキー文字は 1 文字も出力しない(manage-connections ハードルール「表示は常にマスク」)。
776
+ // 実際のキー文字は 1 文字も出力しない(setup-connections ハードルール「表示は常にマスク」)。
777
777
  return secret ? "***configured***" : "***unconfigured***";
778
778
  }
779
779
 
@@ -873,7 +873,7 @@ async function listEngines(runtime = {}) {
873
873
  id: engine.id, label: engine.label, languages: engine.languages, place: engine.place, provider: engine.provider,
874
874
  group: 'cloud', endpoint: engine.endpoint, price: engine.price,
875
875
  availability: directAvailability[engine.provider], default_voice: engine.default_voice,
876
- credit_required: false, supports: engine.supports,
876
+ credit_required: false, supports: engine.supports, ...(engine.candidate ? { candidate: true } : {}),
877
877
  })),
878
878
  ] };
879
879
  }
@@ -956,7 +956,7 @@ async function runDryRun(options, readingText, io) {
956
956
  emit({ dry_run: true, engine: engine.id, output_path: outputPath,
957
957
  estimated_cost_usd: estimateTtsCost(engine, readingText),
958
958
  request: { endpoint: engine.endpoint, headers: engine.provider === 'fish-audio'
959
- ? { Authorization: `Bearer ${maskKey(readProviderKey('FISH_AUDIO_API_KEY'))}`, model: 's2.1-pro',
959
+ ? { Authorization: `Bearer ${maskKey(readProviderKey('FISH_AUDIO_API_KEY'))}`, model: engine.model,
960
960
  'Content-Type': record ? 'application/msgpack' : 'application/json' }
961
961
  : { 'x-goog-api-key': maskKey(readProviderKey('GEMINI_API_KEY')), 'Content-Type': 'application/json' }, body } }, io.log);
962
962
  return;
@@ -1041,6 +1041,10 @@ async function runGenerate(options, io) {
1041
1041
  if (options.engine === 'chatterbox' && readingText.length > 300) {
1042
1042
  throw new PublicError(cliText('cli.narration.error.chatterboxLength'), 2);
1043
1043
  }
1044
+ // 候補は既定で選ばれない。名指しされた場合も、送信前に試聴前であることを 1 行で伝える。
1045
+ if (ttsEngine(options.engine)?.candidate) {
1046
+ io.logError(cliText('cli.narration.warning.candidateEngine', { engine: options.engine }));
1047
+ }
1044
1048
 
1045
1049
  if (options.dryRun) {
1046
1050
  await runDryRun(options, readingText, io);
@@ -1118,7 +1122,7 @@ async function runGenerate(options, io) {
1118
1122
  referenceAudio, referenceText: record?.meta.reference_text });
1119
1123
  audioBuffer = await synthesizeDirect(engine, payload, key);
1120
1124
  provenance = { provider: engine.provider, engine: engine.id,
1121
- model: engine.provider === 'fish-audio' ? 's2.1-pro' : 'gemini-3.8-flash-tts',
1125
+ model: engine.model ?? engine.id,
1122
1126
  voice: record ? `profile:${options.profile}` : options.voice,
1123
1127
  generated_at: new Date().toISOString(), price_verified: engine.price.verified };
1124
1128
  } else if (options.engine === "gemini-tts") {
@@ -12,18 +12,20 @@ const languageTagPattern = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;
12
12
 
13
13
  function parseArguments(args) {
14
14
  let target;
15
+ let scaffold;
15
16
  let template;
16
17
  let from;
17
18
  let lang;
18
19
 
19
20
  for (let index = 0; index < args.length; index += 1) {
20
21
  const argument = args[index];
21
- if (argument === '--template') {
22
+ if (argument === '--template' || argument === '--scaffold') {
22
23
  const value = args[index + 1];
23
24
  if (!value || value.startsWith('--')) {
24
- throw new Error(`${usage()}\n${cliText('cli.new.error.templateMissing', {})}`);
25
+ throw new Error(`${usage()}\n${cliText(argument === '--scaffold' ? 'cli.new.error.scaffoldMissingPath' : 'cli.new.error.templateMissing', {})}`);
25
26
  }
26
- template = value;
27
+ if (argument === '--scaffold') scaffold = value;
28
+ else template = value;
27
29
  index += 1;
28
30
  } else if (argument === '--from') {
29
31
  const value = args[index + 1];
@@ -51,7 +53,10 @@ function parseArguments(args) {
51
53
  if (target === undefined) {
52
54
  throw new Error(usage());
53
55
  }
54
- return { target, template, from, lang };
56
+ if (scaffold !== undefined && template !== undefined) {
57
+ throw new Error(`${usage()}\n${cliText('cli.new.error.scaffoldAndTemplate', {})}`);
58
+ }
59
+ return { target, scaffold: scaffold ?? template, usedTemplateAlias: template !== undefined, from, lang };
55
60
  }
56
61
 
57
62
  async function directoryHasFile(directory, relativeFile) {
@@ -106,9 +111,10 @@ export async function runNewCommand(args, options = {}) {
106
111
  return { exitCode: 1 };
107
112
  }
108
113
 
109
- const { target, template, from, lang } = parsed;
114
+ const { target, scaffold, usedTemplateAlias, from, lang } = parsed;
115
+ if (usedTemplateAlias) logError(text('cli.new.notice.templateAlias'));
110
116
  const targetDir = path.resolve(cwd, target);
111
- const templateDir = template ? path.resolve(cwd, template) : assets.templateDir;
117
+ const templateDir = scaffold ? path.resolve(cwd, scaffold) : assets.templateDir;
112
118
  let playbookDir;
113
119
  if (from) {
114
120
  try { playbookDir = await resolvePlaybookDir(from, { cwd, targetDir, assets, env: options.env }); }
@@ -1,5 +1,6 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
+ import fs from 'node:fs/promises';
3
4
  import { pathToFileURL } from 'node:url';
4
5
 
5
6
  import { cliText } from './i18n.mjs';
@@ -9,13 +10,13 @@ const usage = () => `${cliText('cli.playbook.help.usage')}\n${cliText('cli.playb
9
10
  const safeChannel = value => typeof value === 'string' && value.trim() && value.length <= 64
10
11
  && value !== '.' && !value.includes('..') && !/[/\\<>:"|?*\x00-\x1f]/.test(value);
11
12
 
12
- function parse(args) {
13
+ function parse(args, usageText = usage()) {
13
14
  const action = args[0];
14
15
  if (action === '--help' || action === '-h' || args.includes('--help') || args.includes('-h')) return { action: 'help' };
15
- if (!['list', 'apply', 'template'].includes(action)) throw new Error(usage());
16
+ if (!['list', 'apply', 'template'].includes(action)) throw new Error(usageText);
16
17
  const result = { action, id: action === 'apply' ? args[1] : null, project: action === 'template' ? args[1] : null };
17
- if (action === 'apply' && (!result.id || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.id))) throw new Error(usage());
18
- if (action === 'template' && !result.project) throw new Error(usage());
18
+ if (action === 'apply' && (!result.id || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.id))) throw new Error(usageText);
19
+ if (action === 'template' && !result.project) throw new Error(usageText);
19
20
  for (let i = action === 'apply' || action === 'template' ? 2 : 1; i < args.length; i++) {
20
21
  const arg = args[i];
21
22
  if ((action === 'list' && arg === '--json') || (action === 'apply' && arg === '--dry-run')) {
@@ -23,13 +24,13 @@ function parse(args) {
23
24
  } else if ((action === 'list' && ['--category', '--tag', '--channel'].includes(arg))
24
25
  || (action === 'apply' && arg === '--into')
25
26
  || (action === 'template' && ['--item', '--id', '--name', '--out', '--license-own'].includes(arg))) {
26
- if (!args[i + 1] || args[i + 1].startsWith('--')) throw new Error(usage());
27
+ if (!args[i + 1] || args[i + 1].startsWith('--')) throw new Error(usageText);
27
28
  result[arg.slice(2)] = args[++i];
28
- } else throw new Error(usage());
29
+ } else throw new Error(usageText);
29
30
  }
30
- if (action === 'apply' && !result.into) throw new Error(usage());
31
- if (action === 'template' && (!result.item || !result.id || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.id))) throw new Error(usage());
32
- if (result.channel && !safeChannel(result.channel)) throw new Error(usage());
31
+ if (action === 'apply' && !result.into) throw new Error(usageText);
32
+ if (action === 'template' && (!result.item || !result.id || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.id))) throw new Error(usageText);
33
+ if (result.channel && !safeChannel(result.channel)) throw new Error(usageText);
33
34
  return result;
34
35
  }
35
36
 
@@ -37,9 +38,12 @@ export async function runPlaybookCommand(args, options = {}) {
37
38
  const log = options.log ?? console.log;
38
39
  const error = options.error ?? console.error;
39
40
  let parsed;
40
- try { parsed = parse(args); }
41
+ try { parsed = parse(args, options.usage ?? usage()); }
41
42
  catch (cause) { error(cause.message); return { exitCode: 1 }; }
42
- if (parsed.action === 'help') { log(usage()); return { exitCode: 0 }; }
43
+ if (parsed.action === 'help') { log(options.usage ?? usage()); return { exitCode: 0 }; }
44
+ if (parsed.action === 'template' && !options.suppressTemplateAliasNotice) {
45
+ error(cliText('cli.playbook.template.aliasNotice'));
46
+ }
43
47
  try {
44
48
  const env = options.env ?? process.env;
45
49
  const cwd = options.cwd ?? process.cwd();
@@ -73,11 +77,17 @@ export async function runPlaybookCommand(args, options = {}) {
73
77
  category: parsed.category, tag: parsed.tag,
74
78
  onWarning: (dir, cause) => error(cliText('cli.playbook.warning.broken', { dir, message: cause.message })) });
75
79
  if (parsed.action === 'list') {
76
- if (parsed.json) log(JSON.stringify(entries));
77
- else for (const entry of entries) log(cliText('cli.playbook.list.row', { id: entry.id, name: entry.name,
80
+ const listed = options.listDepth === undefined ? entries : (await Promise.all(entries.map(async entry => {
81
+ try {
82
+ const book = JSON.parse(await fs.readFile(path.join(entry.dir, 'playbook.json'), 'utf8'));
83
+ return book.depth === options.listDepth ? entry : null;
84
+ } catch { return null; }
85
+ }))).filter(Boolean);
86
+ if (parsed.json) log(JSON.stringify(listed));
87
+ else for (const entry of listed) log(cliText('cli.playbook.list.row', { id: entry.id, name: entry.name,
78
88
  category: entry.category, source: entry.source,
79
89
  duration: entry.targetDurationSec ?? cliText('cli.playbook.list.durationUnknown') }));
80
- return { exitCode: 0, entries };
90
+ return { exitCode: 0, entries: listed };
81
91
  }
82
92
  const entry = entries.find(item => item.id === parsed.id);
83
93
  if (!entry) throw new Error(cliText('cli.playbook.error.notFound', { id: parsed.id }));
@@ -0,0 +1,281 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, statSync } from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ import { PREVIEW_SERVER_SCRIPT_RELATIVE, resolveLauncherAssets } from './repo-assets.mjs';
6
+ import { cliText } from './i18n.mjs';
7
+
8
+ /**
9
+ * `akari preview [dir] [--port <port>] [--open]` — プロジェクトのプレビューを
10
+ * ブラウザで開く URL を出す。サーバーの実体は `packages/preview-server/src/server.mjs`
11
+ * (モノレポ checkout / アプリの Resources / 同梱されていれば npm vendor)を子プロセスとして
12
+ * 起動するだけで、ここでサーバーを再実装しない。
13
+ *
14
+ * 既定ポートは preview-server の既定(3000)。`akari.sh --preview` の既定は 4567 だが、
15
+ * シェル版の既存挙動は変えず、Node CLI 側はサーバー本体の既定に合わせる。
16
+ */
17
+
18
+ export const PREVIEW_DEFAULT_PORT = 3000;
19
+
20
+ export function previewHelp() {
21
+ return [
22
+ cliText('cli.preview.help.usage'),
23
+ '',
24
+ cliText('cli.preview.help.description'),
25
+ '',
26
+ cliText('cli.preview.help.port'),
27
+ cliText('cli.preview.help.open'),
28
+ cliText('cli.preview.help.help'),
29
+ ];
30
+ }
31
+
32
+ function isPort(value) {
33
+ return /^\d+$/u.test(value) && Number(value) >= 1 && Number(value) <= 65535;
34
+ }
35
+
36
+ /**
37
+ * 引数を解釈する。`help` は他引数より優先する(`--help` は常に成功で終わる)。
38
+ * `projectRoot` は決して実在確認しない — 呼び出し側が案内付きで止める。
39
+ */
40
+ export function parsePreviewArguments(args, cwd = process.cwd()) {
41
+ const positional = [];
42
+ let port = PREVIEW_DEFAULT_PORT;
43
+ let open = false;
44
+ let help = false;
45
+
46
+ for (let i = 0; i < args.length; i += 1) {
47
+ const arg = args[i];
48
+ if (arg === '-h' || arg === '--help') {
49
+ help = true;
50
+ continue;
51
+ }
52
+ if (arg === '-o' || arg === '--open') {
53
+ open = true;
54
+ continue;
55
+ }
56
+ if (arg === '-p' || arg === '--port') {
57
+ const value = args[i + 1];
58
+ if (value === undefined || value.startsWith('-')) {
59
+ return { ok: false, message: cliText('cli.preview.error.portMissing') };
60
+ }
61
+ i += 1;
62
+ if (!isPort(value)) {
63
+ return { ok: false, message: cliText('cli.preview.error.invalidPort', { port: value }) };
64
+ }
65
+ port = Number(value);
66
+ continue;
67
+ }
68
+ if (arg.startsWith('--port=')) {
69
+ const value = arg.slice('--port='.length);
70
+ if (!isPort(value)) {
71
+ return { ok: false, message: cliText('cli.preview.error.invalidPort', { port: value }) };
72
+ }
73
+ port = Number(value);
74
+ continue;
75
+ }
76
+ if (arg.startsWith('-')) {
77
+ return { ok: false, message: cliText('cli.preview.error.unknownOption', { option: arg }) };
78
+ }
79
+ positional.push(arg);
80
+ }
81
+
82
+ if (help) return { ok: true, help: true };
83
+ if (positional.length > 1) {
84
+ return { ok: false, message: cliText('cli.preview.error.multipleDirs') };
85
+ }
86
+ return {
87
+ ok: true,
88
+ help: false,
89
+ port,
90
+ open,
91
+ projectRoot: path.resolve(cwd, positional[0] ?? '.'),
92
+ };
93
+ }
94
+
95
+ /** サーバーが listen 後に出す URL 行だけを取り出す(banner の他行には反応しない)。 */
96
+ export function extractPreviewUrl(line) {
97
+ const match = /^\s*(http:\/\/(?:127\.0\.0\.1|localhost|\[[^\]]+\]|[^\s:]+):\d+)\s*$/u.exec(line);
98
+ return match ? match[1] : null;
99
+ }
100
+
101
+ function isDirectory(target, stat = statSync) {
102
+ try {
103
+ return stat(target).isDirectory();
104
+ } catch {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ function defaultOpenBrowser(url, platform = process.platform) {
110
+ try {
111
+ const [command, args] = platform === 'win32'
112
+ ? ['cmd', ['/c', 'start', '', url]]
113
+ : [platform === 'darwin' ? 'open' : 'xdg-open', [url]];
114
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
115
+ child.unref();
116
+ return true;
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
122
+ export async function runPreviewCommand(args, options = {}) {
123
+ const log = options.log ?? ((line) => console.log(line));
124
+ const logError = options.error ?? options.logError ?? ((line) => console.error(line));
125
+ const parsed = parsePreviewArguments(args, options.cwd ?? process.cwd());
126
+
127
+ if (!parsed.ok) {
128
+ logError(parsed.message);
129
+ for (const line of previewHelp()) logError(line);
130
+ return { exitCode: 2 };
131
+ }
132
+ if (parsed.help) {
133
+ for (const line of previewHelp()) log(line);
134
+ return { exitCode: 0 };
135
+ }
136
+
137
+ const projectRoot = parsed.projectRoot;
138
+ if (!isDirectory(projectRoot, options.statSync ?? statSync)) {
139
+ logError(cliText('cli.preview.error.directoryMissing', { root: projectRoot }));
140
+ return { exitCode: 1 };
141
+ }
142
+
143
+ if (!(options.existsSync ?? existsSync)(path.join(projectRoot, '.akari'))) {
144
+ logError(cliText('cli.preview.error.notProject', { root: projectRoot }));
145
+ logError(cliText('cli.preview.error.projectGuidance'));
146
+ return { exitCode: 1 };
147
+ }
148
+
149
+ const assets = options.assets ?? resolveLauncherAssets({
150
+ resourcesPath: options.resourcesPath ?? process.resourcesPath,
151
+ });
152
+ const script = assets.previewServerScript;
153
+ if (!script || !(options.existsSync ?? existsSync)(script)) {
154
+ const missing = script ?? PREVIEW_SERVER_SCRIPT_RELATIVE;
155
+ const root = assets.repoRoot ?? cliText('cli.preview.label.unknown');
156
+ logError(cliText('cli.preview.error.serverMissing', { path: missing, root }));
157
+ return { exitCode: 1 };
158
+ }
159
+
160
+ return await startPreviewServer({
161
+ script,
162
+ projectRoot,
163
+ port: parsed.port,
164
+ open: parsed.open,
165
+ log,
166
+ logError,
167
+ env: options.env ?? process.env,
168
+ platform: options.platform ?? process.platform,
169
+ spawn: options.spawn ?? spawn,
170
+ openBrowser: options.openBrowser ?? defaultOpenBrowser,
171
+ });
172
+ }
173
+
174
+ function startPreviewServer({
175
+ script,
176
+ projectRoot,
177
+ port,
178
+ open,
179
+ log,
180
+ logError,
181
+ env,
182
+ platform,
183
+ spawn,
184
+ openBrowser,
185
+ }) {
186
+ return new Promise((resolve) => {
187
+ let child;
188
+ try {
189
+ child = spawn(process.execPath, [script, projectRoot, '--port', String(port)], {
190
+ cwd: projectRoot,
191
+ env,
192
+ stdio: ['ignore', 'pipe', 'pipe'],
193
+ });
194
+ } catch (cause) {
195
+ logError(cliText('cli.preview.error.spawnFailed', {
196
+ message: cause instanceof Error ? cause.message : String(cause),
197
+ }));
198
+ resolve({ exitCode: 1 });
199
+ return;
200
+ }
201
+
202
+ let ready = false;
203
+ let settled = false;
204
+ let outBuffer = '';
205
+ let errBuffer = '';
206
+ const pendingLines = [];
207
+
208
+ const emitLine = (line) => {
209
+ if (!ready) {
210
+ const url = extractPreviewUrl(line);
211
+ if (url) {
212
+ ready = true;
213
+ // URL を 1 行目に出す。banner は URL の後ろへ回し、起動ログも失わない。
214
+ log(url);
215
+ for (const pending of pendingLines) log(pending);
216
+ pendingLines.length = 0;
217
+ if (open) openBrowser(url, platform);
218
+ return true;
219
+ }
220
+ pendingLines.push(line);
221
+ return false;
222
+ }
223
+ log(line);
224
+ return true;
225
+ };
226
+
227
+ const onStdout = (chunk) => {
228
+ outBuffer += chunk;
229
+ let index;
230
+ while ((index = outBuffer.indexOf('\n')) >= 0) {
231
+ const line = outBuffer.slice(0, index).replace(/\r$/u, '');
232
+ outBuffer = outBuffer.slice(index + 1);
233
+ emitLine(line);
234
+ }
235
+ };
236
+
237
+ const onStderr = (chunk) => {
238
+ if (!ready) {
239
+ errBuffer += chunk;
240
+ return;
241
+ }
242
+ logError(chunk.replace(/\r?\n$/u, ''));
243
+ };
244
+
245
+ const onSignal = (signal) => {
246
+ try { child.kill(signal); } catch { /* 既に終了している。 */ }
247
+ };
248
+ const signals = ['SIGINT', 'SIGTERM'];
249
+ for (const signal of signals) process.on(signal, onSignal);
250
+
251
+ const cleanup = () => {
252
+ for (const signal of signals) process.off(signal, onSignal);
253
+ settled = true;
254
+ };
255
+
256
+ child.stdout?.setEncoding?.('utf8');
257
+ child.stderr?.setEncoding?.('utf8');
258
+ child.stdout?.on('data', onStdout);
259
+ child.stderr?.on('data', onStderr);
260
+
261
+ const finish = (code) => {
262
+ if (settled) return;
263
+ cleanup();
264
+ if (!ready) {
265
+ for (const line of pendingLines) logError(line);
266
+ if (outBuffer) logError(outBuffer);
267
+ if (errBuffer) logError(errBuffer.replace(/\r?\n$/u, ''));
268
+ logError(cliText('cli.preview.error.startFailed', { code: code ?? 1 }));
269
+ }
270
+ resolve({ exitCode: typeof code === 'number' ? code : 0 });
271
+ };
272
+
273
+ child.on('error', (cause) => {
274
+ logError(cliText('cli.preview.error.spawnFailed', {
275
+ message: cause instanceof Error ? cause.message : String(cause),
276
+ }));
277
+ finish(1);
278
+ });
279
+ child.on('exit', (code) => finish(code));
280
+ });
281
+ }