@vibeframe/cli 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-lint.log +21 -0
- package/.turbo/turbo-test.log +689 -0
- package/dist/agent/adapters/claude.d.ts +15 -0
- package/dist/agent/adapters/claude.d.ts.map +1 -0
- package/dist/agent/adapters/claude.js +119 -0
- package/dist/agent/adapters/claude.js.map +1 -0
- package/dist/agent/adapters/gemini.d.ts +15 -0
- package/dist/agent/adapters/gemini.d.ts.map +1 -0
- package/dist/agent/adapters/gemini.js +132 -0
- package/dist/agent/adapters/gemini.js.map +1 -0
- package/dist/agent/adapters/index.d.ts +27 -0
- package/dist/agent/adapters/index.d.ts.map +1 -0
- package/dist/agent/adapters/index.js +38 -0
- package/dist/agent/adapters/index.js.map +1 -0
- package/dist/agent/adapters/ollama.d.ts +20 -0
- package/dist/agent/adapters/ollama.d.ts.map +1 -0
- package/dist/agent/adapters/ollama.js +186 -0
- package/dist/agent/adapters/ollama.js.map +1 -0
- package/dist/agent/adapters/openai.d.ts +15 -0
- package/dist/agent/adapters/openai.d.ts.map +1 -0
- package/dist/agent/adapters/openai.js +92 -0
- package/dist/agent/adapters/openai.js.map +1 -0
- package/dist/agent/adapters/xai.d.ts +15 -0
- package/dist/agent/adapters/xai.d.ts.map +1 -0
- package/dist/agent/adapters/xai.js +95 -0
- package/dist/agent/adapters/xai.js.map +1 -0
- package/dist/agent/index.d.ts +69 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +180 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/memory/index.d.ts +70 -0
- package/dist/agent/memory/index.d.ts.map +1 -0
- package/dist/agent/memory/index.js +132 -0
- package/dist/agent/memory/index.js.map +1 -0
- package/dist/agent/prompts/system.d.ts +6 -0
- package/dist/agent/prompts/system.d.ts.map +1 -0
- package/dist/agent/prompts/system.js +103 -0
- package/dist/agent/prompts/system.js.map +1 -0
- package/dist/agent/tools/ai-editing.d.ts +15 -0
- package/dist/agent/tools/ai-editing.d.ts.map +1 -0
- package/dist/agent/tools/ai-editing.js +763 -0
- package/dist/agent/tools/ai-editing.js.map +1 -0
- package/dist/agent/tools/ai-generation.d.ts +13 -0
- package/dist/agent/tools/ai-generation.d.ts.map +1 -0
- package/dist/agent/tools/ai-generation.js +973 -0
- package/dist/agent/tools/ai-generation.js.map +1 -0
- package/dist/agent/tools/ai-pipeline.d.ts +14 -0
- package/dist/agent/tools/ai-pipeline.d.ts.map +1 -0
- package/dist/agent/tools/ai-pipeline.js +961 -0
- package/dist/agent/tools/ai-pipeline.js.map +1 -0
- package/dist/agent/tools/ai.d.ts +13 -0
- package/dist/agent/tools/ai.d.ts.map +1 -0
- package/dist/agent/tools/ai.js +19 -0
- package/dist/agent/tools/ai.js.map +1 -0
- package/dist/agent/tools/batch.d.ts +6 -0
- package/dist/agent/tools/batch.d.ts.map +1 -0
- package/dist/agent/tools/batch.js +383 -0
- package/dist/agent/tools/batch.js.map +1 -0
- package/dist/agent/tools/e2e.test.d.ts +26 -0
- package/dist/agent/tools/e2e.test.d.ts.map +1 -0
- package/dist/agent/tools/e2e.test.js +397 -0
- package/dist/agent/tools/e2e.test.js.map +1 -0
- package/dist/agent/tools/export.d.ts +6 -0
- package/dist/agent/tools/export.d.ts.map +1 -0
- package/dist/agent/tools/export.js +171 -0
- package/dist/agent/tools/export.js.map +1 -0
- package/dist/agent/tools/filesystem.d.ts +6 -0
- package/dist/agent/tools/filesystem.d.ts.map +1 -0
- package/dist/agent/tools/filesystem.js +212 -0
- package/dist/agent/tools/filesystem.js.map +1 -0
- package/dist/agent/tools/index.d.ts +65 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +120 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/tools/integration.test.d.ts +11 -0
- package/dist/agent/tools/integration.test.d.ts.map +1 -0
- package/dist/agent/tools/integration.test.js +659 -0
- package/dist/agent/tools/integration.test.js.map +1 -0
- package/dist/agent/tools/media.d.ts +6 -0
- package/dist/agent/tools/media.d.ts.map +1 -0
- package/dist/agent/tools/media.js +616 -0
- package/dist/agent/tools/media.js.map +1 -0
- package/dist/agent/tools/project.d.ts +6 -0
- package/dist/agent/tools/project.d.ts.map +1 -0
- package/dist/agent/tools/project.js +284 -0
- package/dist/agent/tools/project.js.map +1 -0
- package/dist/agent/tools/timeline.d.ts +6 -0
- package/dist/agent/tools/timeline.d.ts.map +1 -0
- package/dist/agent/tools/timeline.js +873 -0
- package/dist/agent/tools/timeline.js.map +1 -0
- package/dist/agent/types.d.ts +59 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/commands/agent.d.ts +21 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +290 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/ai-analyze.d.ts +106 -0
- package/dist/commands/ai-analyze.d.ts.map +1 -0
- package/dist/commands/ai-analyze.js +327 -0
- package/dist/commands/ai-analyze.js.map +1 -0
- package/dist/commands/ai-animated-caption.d.ts +64 -0
- package/dist/commands/ai-animated-caption.d.ts.map +1 -0
- package/dist/commands/ai-animated-caption.js +272 -0
- package/dist/commands/ai-animated-caption.js.map +1 -0
- package/dist/commands/ai-audio.d.ts +20 -0
- package/dist/commands/ai-audio.d.ts.map +1 -0
- package/dist/commands/ai-audio.js +808 -0
- package/dist/commands/ai-audio.js.map +1 -0
- package/dist/commands/ai-broll.d.ts +15 -0
- package/dist/commands/ai-broll.d.ts.map +1 -0
- package/dist/commands/ai-broll.js +406 -0
- package/dist/commands/ai-broll.js.map +1 -0
- package/dist/commands/ai-edit-cli.d.ts +14 -0
- package/dist/commands/ai-edit-cli.d.ts.map +1 -0
- package/dist/commands/ai-edit-cli.js +579 -0
- package/dist/commands/ai-edit-cli.js.map +1 -0
- package/dist/commands/ai-edit.d.ts +398 -0
- package/dist/commands/ai-edit.d.ts.map +1 -0
- package/dist/commands/ai-edit.js +1019 -0
- package/dist/commands/ai-edit.js.map +1 -0
- package/dist/commands/ai-fill-gaps.d.ts +14 -0
- package/dist/commands/ai-fill-gaps.d.ts.map +1 -0
- package/dist/commands/ai-fill-gaps.js +451 -0
- package/dist/commands/ai-fill-gaps.js.map +1 -0
- package/dist/commands/ai-helpers.d.ts +20 -0
- package/dist/commands/ai-helpers.d.ts.map +1 -0
- package/dist/commands/ai-helpers.js +59 -0
- package/dist/commands/ai-helpers.js.map +1 -0
- package/dist/commands/ai-highlights.d.ts +127 -0
- package/dist/commands/ai-highlights.d.ts.map +1 -0
- package/dist/commands/ai-highlights.js +1026 -0
- package/dist/commands/ai-highlights.js.map +1 -0
- package/dist/commands/ai-image.d.ts +34 -0
- package/dist/commands/ai-image.d.ts.map +1 -0
- package/dist/commands/ai-image.js +653 -0
- package/dist/commands/ai-image.js.map +1 -0
- package/dist/commands/ai-motion.d.ts +50 -0
- package/dist/commands/ai-motion.d.ts.map +1 -0
- package/dist/commands/ai-motion.js +271 -0
- package/dist/commands/ai-motion.js.map +1 -0
- package/dist/commands/ai-narrate.d.ts +66 -0
- package/dist/commands/ai-narrate.d.ts.map +1 -0
- package/dist/commands/ai-narrate.js +329 -0
- package/dist/commands/ai-narrate.js.map +1 -0
- package/dist/commands/ai-review.d.ts +57 -0
- package/dist/commands/ai-review.d.ts.map +1 -0
- package/dist/commands/ai-review.js +251 -0
- package/dist/commands/ai-review.js.map +1 -0
- package/dist/commands/ai-script-pipeline-cli.d.ts +9 -0
- package/dist/commands/ai-script-pipeline-cli.d.ts.map +1 -0
- package/dist/commands/ai-script-pipeline-cli.js +1494 -0
- package/dist/commands/ai-script-pipeline-cli.js.map +1 -0
- package/dist/commands/ai-script-pipeline.d.ts +259 -0
- package/dist/commands/ai-script-pipeline.d.ts.map +1 -0
- package/dist/commands/ai-script-pipeline.js +1027 -0
- package/dist/commands/ai-script-pipeline.js.map +1 -0
- package/dist/commands/ai-suggest-edit.d.ts +14 -0
- package/dist/commands/ai-suggest-edit.d.ts.map +1 -0
- package/dist/commands/ai-suggest-edit.js +220 -0
- package/dist/commands/ai-suggest-edit.js.map +1 -0
- package/dist/commands/ai-video-fx.d.ts +14 -0
- package/dist/commands/ai-video-fx.d.ts.map +1 -0
- package/dist/commands/ai-video-fx.js +395 -0
- package/dist/commands/ai-video-fx.js.map +1 -0
- package/dist/commands/ai-video.d.ts +15 -0
- package/dist/commands/ai-video.d.ts.map +1 -0
- package/dist/commands/ai-video.js +785 -0
- package/dist/commands/ai-video.js.map +1 -0
- package/dist/commands/ai-viral.d.ts +15 -0
- package/dist/commands/ai-viral.d.ts.map +1 -0
- package/dist/commands/ai-viral.js +519 -0
- package/dist/commands/ai-viral.js.map +1 -0
- package/dist/commands/ai-visual-fx.d.ts +14 -0
- package/dist/commands/ai-visual-fx.d.ts.map +1 -0
- package/dist/commands/ai-visual-fx.js +505 -0
- package/dist/commands/ai-visual-fx.js.map +1 -0
- package/dist/commands/ai.d.ts +38 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +225 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/ai.test.d.ts +2 -0
- package/dist/commands/ai.test.d.ts.map +1 -0
- package/dist/commands/ai.test.js +554 -0
- package/dist/commands/ai.test.js.map +1 -0
- package/dist/commands/analyze.d.ts +16 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +247 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/audio.d.ts +18 -0
- package/dist/commands/audio.d.ts.map +1 -0
- package/dist/commands/audio.js +539 -0
- package/dist/commands/audio.js.map +1 -0
- package/dist/commands/batch.d.ts +3 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +366 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/batch.test.d.ts +2 -0
- package/dist/commands/batch.test.d.ts.map +1 -0
- package/dist/commands/batch.test.js +203 -0
- package/dist/commands/batch.test.js.map +1 -0
- package/dist/commands/detect.d.ts +3 -0
- package/dist/commands/detect.d.ts.map +1 -0
- package/dist/commands/detect.js +273 -0
- package/dist/commands/detect.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +191 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit-cmd.d.ts +26 -0
- package/dist/commands/edit-cmd.d.ts.map +1 -0
- package/dist/commands/edit-cmd.js +870 -0
- package/dist/commands/edit-cmd.js.map +1 -0
- package/dist/commands/export.d.ts +39 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +730 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/generate.d.ts +25 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +1885 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/media.d.ts +3 -0
- package/dist/commands/media.d.ts.map +1 -0
- package/dist/commands/media.js +165 -0
- package/dist/commands/media.js.map +1 -0
- package/dist/commands/output.d.ts +45 -0
- package/dist/commands/output.d.ts.map +1 -0
- package/dist/commands/output.js +122 -0
- package/dist/commands/output.js.map +1 -0
- package/dist/commands/pipeline.d.ts +19 -0
- package/dist/commands/pipeline.d.ts.map +1 -0
- package/dist/commands/pipeline.js +345 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +139 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/project.test.d.ts +2 -0
- package/dist/commands/project.test.d.ts.map +1 -0
- package/dist/commands/project.test.js +105 -0
- package/dist/commands/project.test.js.map +1 -0
- package/dist/commands/sanitize.d.ts +21 -0
- package/dist/commands/sanitize.d.ts.map +1 -0
- package/dist/commands/sanitize.js +56 -0
- package/dist/commands/sanitize.js.map +1 -0
- package/dist/commands/schema.d.ts +11 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +101 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +440 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/timeline.d.ts +3 -0
- package/dist/commands/timeline.d.ts.map +1 -0
- package/dist/commands/timeline.js +469 -0
- package/dist/commands/timeline.js.map +1 -0
- package/dist/commands/timeline.test.d.ts +2 -0
- package/dist/commands/timeline.test.d.ts.map +1 -0
- package/dist/commands/timeline.test.js +320 -0
- package/dist/commands/timeline.test.js.map +1 -0
- package/dist/commands/validate.d.ts +32 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +63 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config/config.test.d.ts +2 -0
- package/dist/config/config.test.d.ts.map +1 -0
- package/dist/config/config.test.js +164 -0
- package/dist/config/config.test.js.map +1 -0
- package/dist/config/index.d.ts +35 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +101 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +43 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +42 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/engine/index.d.ts +3 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/project.d.ts +84 -0
- package/dist/engine/project.d.ts.map +1 -0
- package/dist/engine/project.js +355 -0
- package/dist/engine/project.js.map +1 -0
- package/dist/engine/project.test.d.ts +2 -0
- package/dist/engine/project.test.d.ts.map +1 -0
- package/dist/engine/project.test.js +599 -0
- package/dist/engine/project.test.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api-key.d.ts +36 -0
- package/dist/utils/api-key.d.ts.map +1 -0
- package/dist/utils/api-key.js +211 -0
- package/dist/utils/api-key.js.map +1 -0
- package/dist/utils/api-key.test.d.ts +2 -0
- package/dist/utils/api-key.test.d.ts.map +1 -0
- package/dist/utils/api-key.test.js +35 -0
- package/dist/utils/api-key.test.js.map +1 -0
- package/dist/utils/audio.d.ts +23 -0
- package/dist/utils/audio.d.ts.map +1 -0
- package/dist/utils/audio.js +79 -0
- package/dist/utils/audio.js.map +1 -0
- package/dist/utils/exec-safe.d.ts +22 -0
- package/dist/utils/exec-safe.d.ts.map +1 -0
- package/dist/utils/exec-safe.js +62 -0
- package/dist/utils/exec-safe.js.map +1 -0
- package/dist/utils/first-run.d.ts +13 -0
- package/dist/utils/first-run.d.ts.map +1 -0
- package/dist/utils/first-run.js +48 -0
- package/dist/utils/first-run.js.map +1 -0
- package/dist/utils/provider-resolver.d.ts +15 -0
- package/dist/utils/provider-resolver.d.ts.map +1 -0
- package/dist/utils/provider-resolver.js +42 -0
- package/dist/utils/provider-resolver.js.map +1 -0
- package/dist/utils/remotion.d.ts +210 -0
- package/dist/utils/remotion.d.ts.map +1 -0
- package/dist/utils/remotion.js +731 -0
- package/dist/utils/remotion.js.map +1 -0
- package/dist/utils/subtitle.d.ts +65 -0
- package/dist/utils/subtitle.d.ts.map +1 -0
- package/dist/utils/subtitle.js +135 -0
- package/dist/utils/subtitle.js.map +1 -0
- package/dist/utils/subtitle.test.d.ts +2 -0
- package/dist/utils/subtitle.test.d.ts.map +1 -0
- package/dist/utils/subtitle.test.js +175 -0
- package/dist/utils/subtitle.test.js.map +1 -0
- package/dist/utils/tty.d.ts +45 -0
- package/dist/utils/tty.d.ts.map +1 -0
- package/dist/utils/tty.js +172 -0
- package/dist/utils/tty.js.map +1 -0
- package/package.json +102 -0
- package/src/agent/adapters/claude.ts +143 -0
- package/src/agent/adapters/gemini.ts +159 -0
- package/src/agent/adapters/index.ts +61 -0
- package/src/agent/adapters/ollama.ts +231 -0
- package/src/agent/adapters/openai.ts +116 -0
- package/src/agent/adapters/xai.ts +119 -0
- package/src/agent/index.ts +251 -0
- package/src/agent/memory/index.ts +151 -0
- package/src/agent/prompts/system.ts +106 -0
- package/src/agent/tools/ai-editing.ts +845 -0
- package/src/agent/tools/ai-generation.ts +1073 -0
- package/src/agent/tools/ai-pipeline.ts +1055 -0
- package/src/agent/tools/ai.ts +21 -0
- package/src/agent/tools/batch.ts +429 -0
- package/src/agent/tools/e2e.test.ts +545 -0
- package/src/agent/tools/export.ts +184 -0
- package/src/agent/tools/filesystem.ts +237 -0
- package/src/agent/tools/index.ts +150 -0
- package/src/agent/tools/integration.test.ts +775 -0
- package/src/agent/tools/media.ts +697 -0
- package/src/agent/tools/project.ts +313 -0
- package/src/agent/tools/timeline.ts +951 -0
- package/src/agent/types.ts +68 -0
- package/src/commands/agent.ts +340 -0
- package/src/commands/ai-analyze.ts +429 -0
- package/src/commands/ai-animated-caption.ts +390 -0
- package/src/commands/ai-audio.ts +941 -0
- package/src/commands/ai-broll.ts +490 -0
- package/src/commands/ai-edit-cli.ts +658 -0
- package/src/commands/ai-edit.ts +1542 -0
- package/src/commands/ai-fill-gaps.ts +566 -0
- package/src/commands/ai-helpers.ts +65 -0
- package/src/commands/ai-highlights.ts +1303 -0
- package/src/commands/ai-image.ts +761 -0
- package/src/commands/ai-motion.ts +347 -0
- package/src/commands/ai-narrate.ts +451 -0
- package/src/commands/ai-review.ts +309 -0
- package/src/commands/ai-script-pipeline-cli.ts +1710 -0
- package/src/commands/ai-script-pipeline.ts +1365 -0
- package/src/commands/ai-suggest-edit.ts +264 -0
- package/src/commands/ai-video-fx.ts +445 -0
- package/src/commands/ai-video.ts +915 -0
- package/src/commands/ai-viral.ts +595 -0
- package/src/commands/ai-visual-fx.ts +601 -0
- package/src/commands/ai.test.ts +627 -0
- package/src/commands/ai.ts +307 -0
- package/src/commands/analyze.ts +282 -0
- package/src/commands/audio.ts +644 -0
- package/src/commands/batch.test.ts +279 -0
- package/src/commands/batch.ts +440 -0
- package/src/commands/detect.ts +329 -0
- package/src/commands/doctor.ts +237 -0
- package/src/commands/edit-cmd.ts +1014 -0
- package/src/commands/export.ts +918 -0
- package/src/commands/generate.ts +2146 -0
- package/src/commands/media.ts +177 -0
- package/src/commands/output.ts +142 -0
- package/src/commands/pipeline.ts +398 -0
- package/src/commands/project.test.ts +127 -0
- package/src/commands/project.ts +149 -0
- package/src/commands/sanitize.ts +60 -0
- package/src/commands/schema.ts +130 -0
- package/src/commands/setup.ts +509 -0
- package/src/commands/timeline.test.ts +499 -0
- package/src/commands/timeline.ts +529 -0
- package/src/commands/validate.ts +77 -0
- package/src/config/config.test.ts +197 -0
- package/src/config/index.ts +125 -0
- package/src/config/schema.ts +82 -0
- package/src/engine/index.ts +2 -0
- package/src/engine/project.test.ts +702 -0
- package/src/engine/project.ts +439 -0
- package/src/index.ts +146 -0
- package/src/utils/api-key.test.ts +41 -0
- package/src/utils/api-key.ts +247 -0
- package/src/utils/audio.ts +83 -0
- package/src/utils/exec-safe.ts +75 -0
- package/src/utils/first-run.ts +52 -0
- package/src/utils/provider-resolver.ts +56 -0
- package/src/utils/remotion.ts +951 -0
- package/src/utils/subtitle.test.ts +227 -0
- package/src/utils/subtitle.ts +169 -0
- package/src/utils/tty.ts +196 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
detectFormat,
|
|
4
|
+
formatTranscript,
|
|
5
|
+
formatSRT,
|
|
6
|
+
formatVTT,
|
|
7
|
+
formatSRTTime,
|
|
8
|
+
formatVTTTime,
|
|
9
|
+
parseSRT,
|
|
10
|
+
} from "./subtitle.js";
|
|
11
|
+
|
|
12
|
+
describe("subtitle utilities", () => {
|
|
13
|
+
describe("detectFormat", () => {
|
|
14
|
+
it("detects SRT from file extension", () => {
|
|
15
|
+
expect(detectFormat("output.srt")).toBe("srt");
|
|
16
|
+
expect(detectFormat("video.SRT")).toBe("srt");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("detects VTT from file extension", () => {
|
|
20
|
+
expect(detectFormat("output.vtt")).toBe("vtt");
|
|
21
|
+
expect(detectFormat("video.VTT")).toBe("vtt");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("defaults to JSON for unknown extensions", () => {
|
|
25
|
+
expect(detectFormat("output.json")).toBe("json");
|
|
26
|
+
expect(detectFormat("output.txt")).toBe("json");
|
|
27
|
+
expect(detectFormat("output")).toBe("json");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("uses explicit format over extension", () => {
|
|
31
|
+
expect(detectFormat("output.json", "srt")).toBe("srt");
|
|
32
|
+
expect(detectFormat("output.srt", "vtt")).toBe("vtt");
|
|
33
|
+
expect(detectFormat("output.vtt", "json")).toBe("json");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("formatSRTTime", () => {
|
|
38
|
+
it("formats zero", () => {
|
|
39
|
+
expect(formatSRTTime(0)).toBe("00:00:00,000");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("formats seconds", () => {
|
|
43
|
+
expect(formatSRTTime(5.5)).toBe("00:00:05,500");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("formats minutes", () => {
|
|
47
|
+
expect(formatSRTTime(65.25)).toBe("00:01:05,250");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("formats hours", () => {
|
|
51
|
+
expect(formatSRTTime(3661.123)).toBe("01:01:01,123");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("formatVTTTime", () => {
|
|
56
|
+
it("formats zero", () => {
|
|
57
|
+
expect(formatVTTTime(0)).toBe("00:00:00.000");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("formats seconds with period separator", () => {
|
|
61
|
+
expect(formatVTTTime(5.5)).toBe("00:00:05.500");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("formats hours", () => {
|
|
65
|
+
expect(formatVTTTime(3661.123)).toBe("01:01:01.123");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("formatSRT", () => {
|
|
70
|
+
it("formats empty segments", () => {
|
|
71
|
+
expect(formatSRT([])).toBe("");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("formats single segment", () => {
|
|
75
|
+
const segments = [
|
|
76
|
+
{ startTime: 0, endTime: 2.5, text: "Hello world" },
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const result = formatSRT(segments);
|
|
80
|
+
|
|
81
|
+
expect(result).toBe(
|
|
82
|
+
"1\n00:00:00,000 --> 00:00:02,500\nHello world\n"
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("formats multiple segments", () => {
|
|
87
|
+
const segments = [
|
|
88
|
+
{ startTime: 0, endTime: 2.5, text: "First line" },
|
|
89
|
+
{ startTime: 2.5, endTime: 5.0, text: "Second line" },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const result = formatSRT(segments);
|
|
93
|
+
|
|
94
|
+
expect(result).toContain("1\n00:00:00,000 --> 00:00:02,500\nFirst line\n");
|
|
95
|
+
expect(result).toContain("2\n00:00:02,500 --> 00:00:05,000\nSecond line\n");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("formatVTT", () => {
|
|
100
|
+
it("includes WEBVTT header", () => {
|
|
101
|
+
const result = formatVTT([]);
|
|
102
|
+
expect(result).toBe("WEBVTT\n\n");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("formats segments with period separator", () => {
|
|
106
|
+
const segments = [
|
|
107
|
+
{ startTime: 0, endTime: 2.5, text: "Hello world" },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
const result = formatVTT(segments);
|
|
111
|
+
|
|
112
|
+
expect(result).toBe(
|
|
113
|
+
"WEBVTT\n\n1\n00:00:00.000 --> 00:00:02.500\nHello world\n"
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("formatTranscript", () => {
|
|
119
|
+
const mockResult = {
|
|
120
|
+
id: "test-id",
|
|
121
|
+
status: "completed",
|
|
122
|
+
fullText: "Hello world. This is a test.",
|
|
123
|
+
segments: [
|
|
124
|
+
{ startTime: 0, endTime: 2.5, text: "Hello world." },
|
|
125
|
+
{ startTime: 2.5, endTime: 5.0, text: "This is a test." },
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
it("formats as JSON", () => {
|
|
130
|
+
const result = formatTranscript(mockResult, "json");
|
|
131
|
+
const parsed = JSON.parse(result);
|
|
132
|
+
|
|
133
|
+
expect(parsed.id).toBe("test-id");
|
|
134
|
+
expect(parsed.segments).toHaveLength(2);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("formats as SRT", () => {
|
|
138
|
+
const result = formatTranscript(mockResult, "srt");
|
|
139
|
+
|
|
140
|
+
expect(result).toContain("00:00:00,000 --> 00:00:02,500");
|
|
141
|
+
expect(result).toContain("Hello world.");
|
|
142
|
+
expect(result).not.toContain("WEBVTT");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("formats as VTT", () => {
|
|
146
|
+
const result = formatTranscript(mockResult, "vtt");
|
|
147
|
+
|
|
148
|
+
expect(result).toContain("WEBVTT");
|
|
149
|
+
expect(result).toContain("00:00:00.000 --> 00:00:02.500");
|
|
150
|
+
expect(result).toContain("Hello world.");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("handles empty segments", () => {
|
|
154
|
+
const emptyResult = { id: "test", status: "completed" };
|
|
155
|
+
|
|
156
|
+
expect(formatTranscript(emptyResult, "srt")).toBe("");
|
|
157
|
+
expect(formatTranscript(emptyResult, "vtt")).toBe("WEBVTT\n\n");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("parseSRT", () => {
|
|
162
|
+
it("parses valid SRT content", () => {
|
|
163
|
+
const srt = `1
|
|
164
|
+
00:00:00,000 --> 00:00:02,500
|
|
165
|
+
Hello world
|
|
166
|
+
|
|
167
|
+
2
|
|
168
|
+
00:00:02,500 --> 00:00:05,000
|
|
169
|
+
This is a test
|
|
170
|
+
`;
|
|
171
|
+
const segments = parseSRT(srt);
|
|
172
|
+
|
|
173
|
+
expect(segments).toHaveLength(2);
|
|
174
|
+
expect(segments[0].startTime).toBe(0);
|
|
175
|
+
expect(segments[0].endTime).toBe(2.5);
|
|
176
|
+
expect(segments[0].text).toBe("Hello world");
|
|
177
|
+
expect(segments[1].startTime).toBe(2.5);
|
|
178
|
+
expect(segments[1].endTime).toBe(5);
|
|
179
|
+
expect(segments[1].text).toBe("This is a test");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("parses multi-line subtitle text", () => {
|
|
183
|
+
const srt = `1
|
|
184
|
+
00:00:00,000 --> 00:00:03,000
|
|
185
|
+
First line
|
|
186
|
+
Second line
|
|
187
|
+
`;
|
|
188
|
+
const segments = parseSRT(srt);
|
|
189
|
+
|
|
190
|
+
expect(segments).toHaveLength(1);
|
|
191
|
+
expect(segments[0].text).toBe("First line\nSecond line");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("returns empty array for empty content", () => {
|
|
195
|
+
expect(parseSRT("")).toHaveLength(0);
|
|
196
|
+
expect(parseSRT(" \n \n ")).toHaveLength(0);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("handles timestamps with hours", () => {
|
|
200
|
+
const srt = `1
|
|
201
|
+
01:30:15,500 --> 02:00:00,000
|
|
202
|
+
Long video subtitle
|
|
203
|
+
`;
|
|
204
|
+
const segments = parseSRT(srt);
|
|
205
|
+
|
|
206
|
+
expect(segments).toHaveLength(1);
|
|
207
|
+
expect(segments[0].startTime).toBe(5415.5); // 1*3600 + 30*60 + 15 + 0.5
|
|
208
|
+
expect(segments[0].endTime).toBe(7200); // 2*3600
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("roundtrips with formatSRT", () => {
|
|
212
|
+
const original = [
|
|
213
|
+
{ startTime: 0, endTime: 2.5, text: "Hello world" },
|
|
214
|
+
{ startTime: 2.5, endTime: 5, text: "Second line" },
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const srtString = formatSRT(original);
|
|
218
|
+
const parsed = parseSRT(srtString);
|
|
219
|
+
|
|
220
|
+
expect(parsed).toHaveLength(2);
|
|
221
|
+
expect(parsed[0].text).toBe("Hello world");
|
|
222
|
+
expect(parsed[1].text).toBe("Second line");
|
|
223
|
+
expect(parsed[0].startTime).toBe(0);
|
|
224
|
+
expect(parsed[0].endTime).toBe(2.5);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subtitle format utilities for SRT/VTT generation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type SubtitleFormat = "json" | "srt" | "vtt";
|
|
6
|
+
|
|
7
|
+
export interface SubtitleSegment {
|
|
8
|
+
startTime: number;
|
|
9
|
+
endTime: number;
|
|
10
|
+
text: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TranscriptResult {
|
|
14
|
+
id: string;
|
|
15
|
+
status: string;
|
|
16
|
+
fullText?: string;
|
|
17
|
+
segments?: SubtitleSegment[];
|
|
18
|
+
detectedLanguage?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Detect subtitle format from file extension or explicit format option
|
|
24
|
+
*/
|
|
25
|
+
export function detectFormat(outputPath: string, explicitFormat?: string): SubtitleFormat {
|
|
26
|
+
if (explicitFormat) {
|
|
27
|
+
const fmt = explicitFormat.toLowerCase();
|
|
28
|
+
if (fmt === "srt" || fmt === "vtt" || fmt === "json") {
|
|
29
|
+
return fmt;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ext = outputPath.toLowerCase().split(".").pop();
|
|
34
|
+
if (ext === "srt") return "srt";
|
|
35
|
+
if (ext === "vtt") return "vtt";
|
|
36
|
+
return "json";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Format transcript result to the specified format
|
|
41
|
+
*/
|
|
42
|
+
export function formatTranscript(result: TranscriptResult, format: SubtitleFormat): string {
|
|
43
|
+
if (format === "json") {
|
|
44
|
+
return JSON.stringify(result, null, 2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const segments = result.segments || [];
|
|
48
|
+
|
|
49
|
+
if (format === "srt") {
|
|
50
|
+
return formatSRT(segments);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return formatVTT(segments);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Format segments as SRT (SubRip Subtitle)
|
|
58
|
+
*
|
|
59
|
+
* SRT Format:
|
|
60
|
+
* 1
|
|
61
|
+
* 00:00:00,000 --> 00:00:02,500
|
|
62
|
+
* Hello world
|
|
63
|
+
*/
|
|
64
|
+
export function formatSRT(segments: SubtitleSegment[]): string {
|
|
65
|
+
return segments.map((seg, index) => {
|
|
66
|
+
const start = formatSRTTime(seg.startTime);
|
|
67
|
+
const end = formatSRTTime(seg.endTime);
|
|
68
|
+
return `${index + 1}\n${start} --> ${end}\n${seg.text}\n`;
|
|
69
|
+
}).join("\n");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format segments as WebVTT (Web Video Text Tracks)
|
|
74
|
+
*
|
|
75
|
+
* VTT Format:
|
|
76
|
+
* WEBVTT
|
|
77
|
+
*
|
|
78
|
+
* 1
|
|
79
|
+
* 00:00:00.000 --> 00:00:02.500
|
|
80
|
+
* Hello world
|
|
81
|
+
*/
|
|
82
|
+
export function formatVTT(segments: SubtitleSegment[]): string {
|
|
83
|
+
const header = "WEBVTT\n\n";
|
|
84
|
+
const cues = segments.map((seg, index) => {
|
|
85
|
+
const start = formatVTTTime(seg.startTime);
|
|
86
|
+
const end = formatVTTTime(seg.endTime);
|
|
87
|
+
return `${index + 1}\n${start} --> ${end}\n${seg.text}\n`;
|
|
88
|
+
}).join("\n");
|
|
89
|
+
return header + cues;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Format time for SRT (uses comma for milliseconds)
|
|
94
|
+
* Format: HH:MM:SS,mmm
|
|
95
|
+
*/
|
|
96
|
+
export function formatSRTTime(seconds: number): string {
|
|
97
|
+
const hours = Math.floor(seconds / 3600);
|
|
98
|
+
const mins = Math.floor((seconds % 3600) / 60);
|
|
99
|
+
const secs = Math.floor(seconds % 60);
|
|
100
|
+
const ms = Math.round((seconds % 1) * 1000);
|
|
101
|
+
|
|
102
|
+
return `${pad(hours, 2)}:${pad(mins, 2)}:${pad(secs, 2)},${pad(ms, 3)}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Format time for VTT (uses period for milliseconds)
|
|
107
|
+
* Format: HH:MM:SS.mmm
|
|
108
|
+
*/
|
|
109
|
+
export function formatVTTTime(seconds: number): string {
|
|
110
|
+
const hours = Math.floor(seconds / 3600);
|
|
111
|
+
const mins = Math.floor((seconds % 3600) / 60);
|
|
112
|
+
const secs = Math.floor(seconds % 60);
|
|
113
|
+
const ms = Math.round((seconds % 1) * 1000);
|
|
114
|
+
|
|
115
|
+
return `${pad(hours, 2)}:${pad(mins, 2)}:${pad(secs, 2)}.${pad(ms, 3)}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Parse SRT content into SubtitleSegment array
|
|
120
|
+
*
|
|
121
|
+
* Handles standard SRT format:
|
|
122
|
+
* 1
|
|
123
|
+
* 00:00:00,000 --> 00:00:02,500
|
|
124
|
+
* Hello world
|
|
125
|
+
*/
|
|
126
|
+
export function parseSRT(content: string): SubtitleSegment[] {
|
|
127
|
+
const segments: SubtitleSegment[] = [];
|
|
128
|
+
const blocks = content.trim().split(/\n\s*\n/);
|
|
129
|
+
|
|
130
|
+
for (const block of blocks) {
|
|
131
|
+
const lines = block.trim().split("\n");
|
|
132
|
+
if (lines.length < 3) continue;
|
|
133
|
+
|
|
134
|
+
// Line 0: sequence number (skip)
|
|
135
|
+
// Line 1: timestamp line
|
|
136
|
+
const timeLine = lines[1].trim();
|
|
137
|
+
const timeMatch = timeLine.match(
|
|
138
|
+
/(\d{2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}[,.]\d{3})/,
|
|
139
|
+
);
|
|
140
|
+
if (!timeMatch) continue;
|
|
141
|
+
|
|
142
|
+
const startTime = parseSRTTimestamp(timeMatch[1]);
|
|
143
|
+
const endTime = parseSRTTimestamp(timeMatch[2]);
|
|
144
|
+
// Lines 2+: subtitle text (may be multi-line)
|
|
145
|
+
const text = lines.slice(2).join("\n").trim();
|
|
146
|
+
|
|
147
|
+
if (text) {
|
|
148
|
+
segments.push({ startTime, endTime, text });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return segments;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseSRTTimestamp(timestamp: string): number {
|
|
156
|
+
// Accept both comma (SRT) and period (VTT) as ms separator
|
|
157
|
+
const normalized = timestamp.replace(",", ".");
|
|
158
|
+
const parts = normalized.split(":");
|
|
159
|
+
const hours = parseInt(parts[0], 10);
|
|
160
|
+
const mins = parseInt(parts[1], 10);
|
|
161
|
+
const secParts = parts[2].split(".");
|
|
162
|
+
const secs = parseInt(secParts[0], 10);
|
|
163
|
+
const ms = parseInt(secParts[1], 10);
|
|
164
|
+
return hours * 3600 + mins * 60 + secs + ms / 1000;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function pad(num: number, size: number): string {
|
|
168
|
+
return num.toString().padStart(size, "0");
|
|
169
|
+
}
|
package/src/utils/tty.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTY Input Utilities
|
|
3
|
+
* Handles stdin fallback to /dev/tty for piped environments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createInterface, Interface } from "node:readline";
|
|
7
|
+
import { ReadStream } from "node:tty";
|
|
8
|
+
|
|
9
|
+
let ttyStream: ReadStream | null = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get a TTY-capable input stream
|
|
13
|
+
* Falls back to /dev/tty when stdin is piped (e.g., from curl)
|
|
14
|
+
*/
|
|
15
|
+
export function getTTYInputStream(): typeof process.stdin | ReadStream {
|
|
16
|
+
if (process.stdin.isTTY) {
|
|
17
|
+
return process.stdin;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// stdin is not a TTY (piped), open /dev/tty directly
|
|
21
|
+
if (!ttyStream) {
|
|
22
|
+
try {
|
|
23
|
+
// Open /dev/tty as a TTY stream
|
|
24
|
+
const fd = require("fs").openSync("/dev/tty", "r");
|
|
25
|
+
ttyStream = new ReadStream(fd);
|
|
26
|
+
} catch {
|
|
27
|
+
// Fallback to stdin if /dev/tty is not available
|
|
28
|
+
console.warn("Warning: Cannot open /dev/tty, interactive input may not work");
|
|
29
|
+
return process.stdin;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return ttyStream;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if we have TTY input available
|
|
37
|
+
*/
|
|
38
|
+
export function hasTTY(): boolean {
|
|
39
|
+
if (process.stdin.isTTY) return true;
|
|
40
|
+
|
|
41
|
+
// Try to actually open /dev/tty to verify it's accessible
|
|
42
|
+
try {
|
|
43
|
+
const fs = require("fs");
|
|
44
|
+
const fd = fs.openSync("/dev/tty", "r");
|
|
45
|
+
fs.closeSync(fd);
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Close the TTY stream if we opened one
|
|
54
|
+
*/
|
|
55
|
+
export function closeTTYStream(): void {
|
|
56
|
+
if (ttyStream) {
|
|
57
|
+
ttyStream.destroy();
|
|
58
|
+
ttyStream = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a readline interface with TTY support
|
|
64
|
+
*/
|
|
65
|
+
export function createTTYInterface(options?: {
|
|
66
|
+
prompt?: string;
|
|
67
|
+
historySize?: number;
|
|
68
|
+
}): Interface {
|
|
69
|
+
const input = getTTYInputStream();
|
|
70
|
+
return createInterface({
|
|
71
|
+
input,
|
|
72
|
+
output: process.stdout,
|
|
73
|
+
terminal: true,
|
|
74
|
+
historySize: options?.historySize ?? 100,
|
|
75
|
+
prompt: options?.prompt ?? "> ",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Prompt for input (single line)
|
|
81
|
+
* Throws in non-TTY environments to prevent hanging.
|
|
82
|
+
*/
|
|
83
|
+
export async function prompt(question: string): Promise<string> {
|
|
84
|
+
if (!hasTTY()) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"Interactive input required but no TTY available. " +
|
|
87
|
+
"Use command flags to provide values non-interactively."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const input = getTTYInputStream();
|
|
91
|
+
const rl = createInterface({
|
|
92
|
+
input,
|
|
93
|
+
output: process.stdout,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
rl.question(question, (answer) => {
|
|
98
|
+
rl.close();
|
|
99
|
+
resolve(answer);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Prompt for hidden input (password/API key)
|
|
106
|
+
* Characters are not echoed to terminal
|
|
107
|
+
*/
|
|
108
|
+
export async function promptHidden(question: string): Promise<string> {
|
|
109
|
+
const input = getTTYInputStream() as ReadStream;
|
|
110
|
+
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
process.stdout.write(question);
|
|
113
|
+
|
|
114
|
+
let value = "";
|
|
115
|
+
|
|
116
|
+
// Check if we can use raw mode
|
|
117
|
+
if (typeof input.setRawMode === "function") {
|
|
118
|
+
input.setRawMode(true);
|
|
119
|
+
input.resume();
|
|
120
|
+
input.setEncoding("utf8");
|
|
121
|
+
|
|
122
|
+
const onData = (char: string) => {
|
|
123
|
+
if (char === "\n" || char === "\r" || char === "\u0004") {
|
|
124
|
+
// Enter or EOF
|
|
125
|
+
input.setRawMode(false);
|
|
126
|
+
input.removeListener("data", onData);
|
|
127
|
+
process.stdout.write("\n");
|
|
128
|
+
resolve(value);
|
|
129
|
+
} else if (char === "\u0003") {
|
|
130
|
+
// Ctrl+C
|
|
131
|
+
input.setRawMode(false);
|
|
132
|
+
process.stdout.write("\n");
|
|
133
|
+
process.exit(1);
|
|
134
|
+
} else if (char === "\u007F" || char === "\b") {
|
|
135
|
+
// Backspace
|
|
136
|
+
if (value.length > 0) {
|
|
137
|
+
value = value.slice(0, -1);
|
|
138
|
+
// Clear character from display
|
|
139
|
+
process.stdout.write("\b \b");
|
|
140
|
+
}
|
|
141
|
+
} else if (char >= " ") {
|
|
142
|
+
// Printable character (may be multiple chars when pasting)
|
|
143
|
+
value += char;
|
|
144
|
+
process.stdout.write("*".repeat(char.length)); // Show asterisk for each char
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
input.on("data", onData);
|
|
149
|
+
} else {
|
|
150
|
+
// Fallback: no raw mode available, input will be visible
|
|
151
|
+
const rl = createInterface({ input, output: process.stdout });
|
|
152
|
+
rl.question("", (answer) => {
|
|
153
|
+
rl.close();
|
|
154
|
+
resolve(answer);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Prompt for selection from a list (1-based index)
|
|
162
|
+
*/
|
|
163
|
+
export async function promptSelect(
|
|
164
|
+
question: string,
|
|
165
|
+
options: string[],
|
|
166
|
+
defaultIndex = 0
|
|
167
|
+
): Promise<number> {
|
|
168
|
+
// Display options
|
|
169
|
+
for (let i = 0; i < options.length; i++) {
|
|
170
|
+
const marker = i === defaultIndex ? "→" : " ";
|
|
171
|
+
console.log(` ${marker} ${i + 1}. ${options[i]}`);
|
|
172
|
+
}
|
|
173
|
+
console.log();
|
|
174
|
+
|
|
175
|
+
const answer = await prompt(question);
|
|
176
|
+
const index = parseInt(answer, 10) - 1;
|
|
177
|
+
|
|
178
|
+
if (isNaN(index) || index < 0 || index >= options.length) {
|
|
179
|
+
return defaultIndex;
|
|
180
|
+
}
|
|
181
|
+
return index;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Prompt for yes/no confirmation
|
|
186
|
+
*/
|
|
187
|
+
export async function promptConfirm(
|
|
188
|
+
question: string,
|
|
189
|
+
defaultYes = true
|
|
190
|
+
): Promise<boolean> {
|
|
191
|
+
const hint = defaultYes ? "(Y/n)" : "(y/N)";
|
|
192
|
+
const answer = await prompt(`${question} ${hint}: `);
|
|
193
|
+
|
|
194
|
+
if (answer === "") return defaultYes;
|
|
195
|
+
return answer.toLowerCase().startsWith("y");
|
|
196
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|