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.
- package/README.md +2 -2
- package/bin/akari.mjs +4 -0
- package/package.json +1 -1
- package/src/cli.mjs +6 -0
- package/src/connect/connectors.json +1 -1
- package/src/lint-command.mjs +85 -0
- package/src/messages.mjs +1 -1
- package/src/narration-command.mjs +11 -7
- package/src/new-command.mjs +12 -6
- package/src/playbook-command.mjs +24 -14
- package/src/preview-command.mjs +281 -0
- package/src/repo-assets.mjs +24 -7
- package/src/status-core/status.mjs +2 -2
- package/src/store-command.mjs +1 -1
- package/src/template-command.mjs +21 -0
- package/src/tts-engines.mjs +39 -18
- package/vendor/.akari-capability-sources.json +34 -32
- package/vendor/catalog/INDEX.md +13 -3
- package/vendor/catalog/audio/candidates-legacy.json +4 -4
- package/vendor/catalog/audio/candidates.html +1 -1
- package/vendor/catalog/audio/candidates.json +2 -2
- package/vendor/docs/contract-2026-10-16-vocabulary-v0.md +5 -3
- package/vendor/packages/akari-launcher/README.md +2 -2
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/akari-tools/bin/speakers.mjs +0 -0
- package/vendor/packages/audio-library-setup/README.md +1 -1
- package/vendor/packages/audio-library-setup/bin/fetch-akari-sounds.mjs +1 -1
- package/vendor/packages/audio-library-setup/bin/generate-candidates-html.mjs +1 -1
- package/vendor/packages/audio-library-setup/shared/akari-sounds.mjs +1 -1
- package/vendor/packages/audio-library-setup/shared/candidates.mjs +4 -4
- package/vendor/packages/creator-root/src/index.mjs +1 -1
- package/vendor/packages/creator-root/test/direct-tts-providers.test.mjs +3 -0
- package/vendor/packages/edit-lint/README.md +19 -1
- package/vendor/packages/edit-lint/src/edit-lint.mjs +143 -20
- package/vendor/packages/edit-lint/src/fix.mjs +252 -0
- package/vendor/packages/edit-lint/src/i18n.mjs +4 -0
- package/vendor/packages/edit-lint/src/lint/box-sizing-messages.json +4 -0
- package/vendor/packages/edit-lint/src/lint/caption-fonts.mjs +139 -0
- package/vendor/packages/edit-lint/src/lint/captions.mjs +5 -3
- package/vendor/packages/edit-lint/src/lint/cuts-tracks.mjs +76 -1
- package/vendor/packages/edit-lint/src/lint/fragment-box-sizing.mjs +385 -0
- package/vendor/packages/edit-lint/src/lint/fragment-css-collision.mjs +73 -0
- package/vendor/packages/edit-lint/src/lint/fragment-css.mjs +74 -0
- package/vendor/packages/edit-lint/src/lint/overlays.mjs +147 -7
- package/vendor/packages/edit-lint/src/lint/review.mjs +1 -1
- package/vendor/packages/edit-store/lib/internal-model.d.ts +15 -8
- package/vendor/packages/edit-store/lib/internal-model.js +94 -28
- package/vendor/packages/edit-store/lib/webview-kernel.js +24 -7
- package/vendor/packages/i18n/locales/en/annotations.json +11 -11
- package/vendor/packages/i18n/locales/en/annotationscommon.json +1 -1
- package/vendor/packages/i18n/locales/en/cli.json +50 -17
- package/vendor/packages/i18n/locales/en/connect.json +1 -1
- package/vendor/packages/i18n/locales/en/inspector.json +12 -12
- package/vendor/packages/i18n/locales/en/lint-messages.json +15 -1
- package/vendor/packages/i18n/locales/en/lint.json +1 -1
- package/vendor/packages/i18n/locales/en/preview.json +1 -1
- package/vendor/packages/i18n/locales/en/project.json +25 -16
- package/vendor/packages/i18n/locales/en/report-cards.json +1 -1
- package/vendor/packages/i18n/locales/en/report-project.json +2 -2
- package/vendor/packages/i18n/locales/en/shell.json +3 -3
- package/vendor/packages/i18n/locales/en/skills.json +26 -24
- package/vendor/packages/i18n/locales/en/surfaces.json +30 -30
- package/vendor/packages/i18n/locales/en/tools.json +13 -1
- package/vendor/packages/i18n/locales/en/transcript.json +8 -8
- package/vendor/packages/i18n/locales/ja/annotations.json +14 -14
- package/vendor/packages/i18n/locales/ja/annotationscommon.json +1 -1
- package/vendor/packages/i18n/locales/ja/cli.json +52 -19
- package/vendor/packages/i18n/locales/ja/connect.json +1 -1
- package/vendor/packages/i18n/locales/ja/inspector.json +13 -13
- package/vendor/packages/i18n/locales/ja/lint-messages.json +15 -1
- package/vendor/packages/i18n/locales/ja/lint.json +1 -1
- package/vendor/packages/i18n/locales/ja/preview.json +1 -1
- package/vendor/packages/i18n/locales/ja/project.json +27 -18
- package/vendor/packages/i18n/locales/ja/report-cards.json +1 -1
- package/vendor/packages/i18n/locales/ja/report-project.json +2 -2
- package/vendor/packages/i18n/locales/ja/shell.json +4 -4
- package/vendor/packages/i18n/locales/ja/skills.json +26 -24
- package/vendor/packages/i18n/locales/ja/surfaces.json +30 -30
- package/vendor/packages/i18n/locales/ja/tools.json +13 -1
- package/vendor/packages/i18n/locales/ja/transcript.json +8 -8
- package/vendor/packages/overlay-runtime/README.md +44 -15
- package/vendor/packages/overlay-runtime/src/three-runtime.js +671 -40
- package/vendor/packages/overlay-runtime/src/vendor/three-bundle.js +978 -289
- package/vendor/packages/project-scaffold/src/index.mjs +34 -8
- package/vendor/packages/schemas/ai-models.json +19 -0
- package/vendor/packages/schemas/ai-models.schema.json +1 -0
- package/vendor/packages/schemas/examples/kit-manifest-v1-name-collision/manifest.json +1 -1
- package/vendor/packages/schemas/examples/kit-manifest-v1-name-collision/skills/{edit-lint → verify-edit}/SKILL.md +1 -1
- package/vendor/packages/schemas/review.schema.json +2 -2
- package/vendor/packages/schemas/skill-renames.mjs +393 -0
- package/vendor/packages/schemas/skill-renames.schema.json +43 -0
- package/vendor/packages/schemas/test/ai-models.test.mjs +20 -2
- package/vendor/packages/schemas/test/kit-manifest.test.mjs +1 -1
- package/vendor/packages/schemas/test/skill-renames.test.mjs +117 -0
- package/vendor/packages/schemas/test/validate-captions.test.mjs +1 -1
- package/vendor/packages/schemas/test/validate-review.test.mjs +2 -2
- package/vendor/presets/luts/INDEX.md +1 -1
- package/vendor/skills/akari/SKILL.md +1 -1
- package/vendor/skills/analyze-footage/bin/person-matte/resolve-packages.mjs +1 -1
- package/vendor/skills/author-scene3d/SKILL.md +45 -0
- package/vendor/skills/{bake-3d/SKILL.md → author-scene3d/bake.md} +11 -25
- package/vendor/skills/{overlay-authoring → author-scene3d}/device-materials.md +3 -3
- package/vendor/skills/author-scene3d/live.md +575 -0
- package/vendor/skills/create-project/SKILL.md +10 -6
- package/vendor/skills/declare-audio/SKILL.md +2 -2
- package/vendor/skills/{address-review → edit-from-review}/SKILL.md +6 -6
- package/vendor/skills/{address-review → edit-from-review}/bin/list.mjs +2 -2
- package/vendor/skills/{address-review → edit-from-review}/bin/respond.mjs +1 -1
- package/vendor/skills/edit-plan/SKILL.md +1 -1
- package/vendor/skills/edit-plan/autonomy.md +2 -2
- package/vendor/skills/edit-plan/avatar-resolution.md +1 -1
- package/vendor/skills/edit-plan/beat-sync.md +3 -3
- package/vendor/skills/edit-plan/execution.md +1 -1
- package/vendor/skills/edit-plan/expression-selection.md +3 -3
- package/vendor/skills/edit-plan/plan-json.md +1 -1
- package/vendor/skills/edit-plan/workflow.md +1 -1
- package/vendor/skills/{beat-sync-edit → edit-to-beat}/SKILL.md +2 -2
- package/vendor/skills/{beat-sync-edit → edit-to-beat}/pitfalls.md +1 -1
- package/vendor/skills/generate-image/SKILL.md +42 -0
- package/vendor/skills/generate-image/image.md +38 -0
- package/vendor/skills/generate-narration/SKILL.md +4 -4
- package/vendor/skills/generate-narration/engines.md +79 -63
- package/vendor/skills/generate-narration/lang/en.md +8 -6
- package/vendor/skills/generate-narration/lang/ja.md +17 -11
- package/vendor/skills/generate-narration/voice-profile-setup.md +3 -3
- package/vendor/skills/{generate-media → generate-video}/SKILL.md +8 -6
- package/vendor/skills/{generate-media → generate-video}/chain.md +1 -1
- package/vendor/skills/generate-video/engines.md +23 -0
- package/vendor/skills/generate-video/still.md +65 -0
- package/vendor/skills/overlay-authoring/3d.md +3 -575
- package/vendor/skills/overlay-authoring/SKILL.md +3 -3
- package/vendor/skills/overlay-authoring/dev-fixtures/test_device_materials.py +1 -1
- package/vendor/skills/overlay-authoring/telop.md +1 -1
- package/vendor/skills/renames.json +16 -0
- package/vendor/skills/render-cut/SKILL.md +2 -2
- package/vendor/skills/research-plan/SKILL.md +1 -1
- package/vendor/skills/research-plan/competitor.md +2 -2
- package/vendor/skills/research-plan/lang/ja.md +1 -1
- package/vendor/skills/research-plan/trends.md +1 -1
- package/vendor/skills/{critique-cut → review-cut}/SKILL.md +6 -6
- package/vendor/skills/{critique-cut → review-cut}/bin/index.js +1 -1
- package/vendor/skills/{critique-cut → review-cut}/workflow.md +3 -3
- package/vendor/skills/{compile-review-session → review-from-recording}/SKILL.md +5 -5
- package/vendor/skills/{compile-review-session → review-from-recording}/bin/compile-review-session.mjs +1 -1
- package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/install-root.mjs +2 -2
- package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/README.md +4 -4
- package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-69-review-json/README.md +6 -6
- package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-69-review-json/reproduce.mjs +1 -1
- package/vendor/skills/review-from-recording/dev-fixtures/issue-79/README.md +3 -0
- package/vendor/skills/{compile-review-session → review-from-recording}/test/helpers/issue-79-fixture.mjs +1 -1
- package/vendor/skills/{compile-review-session → review-from-recording}/test/issue-79-snapshot.test.mjs +1 -1
- package/vendor/skills/setup-chat-approval/SKILL.md +3 -3
- package/vendor/skills/setup-chat-approval/bin/resolve-packages.mjs +1 -1
- package/vendor/skills/setup-chat-approval/setup-token.md +1 -1
- package/vendor/skills/{manage-connections → setup-connections}/SKILL.md +53 -7
- package/vendor/skills/{manage-connections → setup-connections}/bin/doctor.mjs +1 -1
- package/vendor/skills/{manage-connections → setup-connections}/bin/resolve-packages.mjs +1 -1
- package/vendor/skills/setup-connections/dev-fixtures/resolve-packages-newest/README.md +17 -0
- package/vendor/skills/{manage-connections → setup-connections}/dev-fixtures/resolve-packages-newest/reproduce.mjs +2 -2
- package/vendor/skills/{manage-connections → setup-connections}/test/resolve-packages-newest.test.mjs +1 -1
- package/vendor/skills/setup-library/SKILL.md +82 -1
- package/vendor/skills/{setup-audio-library/assisted-fetch.md → setup-library/audio-assisted-fetch.md} +2 -2
- package/vendor/skills/{setup-audio-library/candidate-list.md → setup-library/audio-candidate-list.md} +1 -1
- package/vendor/skills/{setup-audio-library/drop-folder.md → setup-library/audio-drop-folder.md} +3 -3
- package/vendor/skills/{setup-audio-library/gallery.md → setup-library/audio-gallery.md} +1 -1
- package/vendor/skills/setup-library/tools-check.md +1 -1
- package/vendor/skills/{edit-lint → verify-edit}/SKILL.md +2 -2
- package/vendor/skills/{verify → verify-project}/SKILL.md +2 -2
- package/vendor/templates/project-default/.claude/skills/README.md +4 -3
- package/vendor/templates/project-default/AGENTS.md +5 -4
- package/vendor/templates/project-default/CLAUDE.md +5 -4
- package/vendor/skills/compile-review-session/dev-fixtures/issue-79/README.md +0 -3
- package/vendor/skills/connect/SKILL.md +0 -24
- package/vendor/skills/generate-media/engines.md +0 -25
- package/vendor/skills/generate-media/still.md +0 -75
- package/vendor/skills/manage-connections/dev-fixtures/resolve-packages-newest/README.md +0 -17
- package/vendor/skills/setup-audio-library/SKILL.md +0 -96
- /package/vendor/skills/{overlay-authoring → author-scene3d}/scripts/device_materials.py +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/bin/core/review-store.mjs +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/captions.json +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/edit.json +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/dev-fixtures/fixture-project/review.json +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/test/review-loss.test.mjs +0 -0
- /package/vendor/skills/{address-review → edit-from-review}/test/review-store.test.mjs +0 -0
- /package/vendor/skills/{beat-sync-edit → edit-to-beat}/generator.md +0 -0
- /package/vendor/skills/{beat-sync-edit → edit-to-beat}/templates/gen-timeline.mjs +0 -0
- /package/vendor/skills/{beat-sync-edit → edit-to-beat}/verify-loop.md +0 -0
- /package/vendor/skills/{generate-media → generate-video}/video.md +0 -0
- /package/vendor/skills/{critique-cut → review-cut}/bin/used-ranges.mjs +0 -0
- /package/vendor/skills/{critique-cut → review-cut}/bin/used-ranges.test.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/canvas-compiler.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/compiler.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/review-store.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/time-mapping.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/bin/core/transcription.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/compilation-rules.md +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0001/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0002/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0003/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0004/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0005/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0006/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0007/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0008/transcript.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0009/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/audio.wav +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/edit.snapshot.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/events.jsonl +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review/sessions/s-0010/session.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/fixture-project/review.json +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/dev-fixtures/issue-79/reproduce.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/test/canvas-compiler.test.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/test/compiler.test.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/test/install-root.test.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/test/review-loss.test.mjs +0 -0
- /package/vendor/skills/{compile-review-session → review-from-recording}/test/ui-events.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/bin/resolve-connections.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/bin/test/doctor-i18n.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/bin/test/resolve-packages.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/connectors-doctor.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/direct-tts-doctor.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/doctor-key-source.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/fieldreport-report-location.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/resolve-connections-defaults.test.mjs +0 -0
- /package/vendor/skills/{manage-connections → setup-connections}/test/resolve-connections.test.mjs +0 -0
- /package/vendor/skills/{setup-audio-library/first-party.md → setup-library/audio-first-party.md} +0 -0
- /package/vendor/skills/{setup-audio-library/store-unlock.md → setup-library/audio-store-unlock.md} +0 -0
- /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/
|
|
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> ...`(
|
|
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.
|
|
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
|
/**
|
|
@@ -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.
|
|
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 (
|
|
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 文字も出力しない(
|
|
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:
|
|
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.
|
|
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") {
|
package/src/new-command.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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 }); }
|
package/src/playbook-command.mjs
CHANGED
|
@@ -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(
|
|
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(
|
|
18
|
-
if (action === 'template' && !result.project) throw new Error(
|
|
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(
|
|
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(
|
|
29
|
+
} else throw new Error(usageText);
|
|
29
30
|
}
|
|
30
|
-
if (action === 'apply' && !result.into) throw new Error(
|
|
31
|
-
if (action === 'template' && (!result.item || !result.id || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.id))) throw new Error(
|
|
32
|
-
if (result.channel && !safeChannel(result.channel)) throw new Error(
|
|
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
|
-
|
|
77
|
-
|
|
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
|
+
}
|