textflowkit 0.1.9__tar.gz → 0.1.10__tar.gz
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.
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.github/workflows/ci.yml +1 -1
- {textflowkit-0.1.9 → textflowkit-0.1.10}/CHANGELOG.md +55 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/PKG-INFO +97 -20
- {textflowkit-0.1.9 → textflowkit-0.1.10}/README.md +96 -19
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/adapters.md +76 -4
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/architecture.md +46 -3
- textflowkit-0.1.10/docs/assets/architecture-overview.svg +213 -0
- textflowkit-0.1.10/docs/assets/local-ui-flow.svg +155 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/index.html +41 -9
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/install.md +34 -1
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/release-checklist.md +24 -1
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/roadmap.md +24 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/site-deployment.md +10 -2
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/user-manual.md +108 -16
- {textflowkit-0.1.9 → textflowkit-0.1.10}/pyproject.toml +10 -1
- textflowkit-0.1.10/scripts/smoke_installed_wheel.py +436 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/__init__.py +1 -1
- textflowkit-0.1.10/src/textflowkit/ui/__init__.py +13 -0
- textflowkit-0.1.10/src/textflowkit/ui/app.py +579 -0
- textflowkit-0.1.10/src/textflowkit/ui/capabilities.py +195 -0
- textflowkit-0.1.10/src/textflowkit/ui/launcher.py +686 -0
- textflowkit-0.1.10/src/textflowkit/ui/ownership.py +602 -0
- textflowkit-0.1.10/src/textflowkit/ui/paths.py +179 -0
- textflowkit-0.1.10/src/textflowkit/ui/security.py +200 -0
- textflowkit-0.1.10/src/textflowkit/ui/static/app.css +285 -0
- textflowkit-0.1.10/src/textflowkit/ui/static/app.js +756 -0
- textflowkit-0.1.10/src/textflowkit/ui/static/index.html +226 -0
- textflowkit-0.1.10/src/textflowkit/ui/uploads.py +247 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_landing_commands.py +28 -11
- textflowkit-0.1.10/tests/test_ui_assets_and_capabilities.py +294 -0
- textflowkit-0.1.10/tests/test_ui_consoleless_logging.py +88 -0
- textflowkit-0.1.10/tests/test_ui_existing_routes_unchanged.py +86 -0
- textflowkit-0.1.10/tests/test_ui_launcher.py +224 -0
- textflowkit-0.1.10/tests/test_ui_ownership_and_shutdown.py +477 -0
- textflowkit-0.1.10/tests/test_ui_packaging.py +153 -0
- textflowkit-0.1.10/tests/test_ui_real_serve.py +88 -0
- textflowkit-0.1.10/tests/test_ui_security.py +168 -0
- textflowkit-0.1.10/tests/test_ui_submission_and_download.py +260 -0
- textflowkit-0.1.10/tests/test_ui_uploads.py +201 -0
- textflowkit-0.1.9/docs/assets/architecture-overview.svg +0 -189
- textflowkit-0.1.9/scripts/smoke_installed_wheel.py +0 -219
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.dockerignore +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.env.example +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.gitattributes +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.github/dependabot.yml +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.github/workflows/publish-pypi.yml +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.gitignore +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/.pre-commit-config.yaml +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/CONTRIBUTING.md +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/Dockerfile +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/LEGAL.md +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/LICENSE +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/SECURITY.md +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/compose.yaml +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/.nojekyll +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/404.html +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/assets/whistle-resume.svg +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/docs/sources.md +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/fetch_published_fonts.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/smoke_live_youtube.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/verify_readme_release.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/verify_release_ci.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/verify_release_versions.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/scripts/write_release_manifest.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/adapters/__init__.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/adapters/http_server.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/adapters/mcp_server.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/assets/README.md +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/assets/selftest-speech.wav +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/cli.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/__init__.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/batch.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/bind.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/cancel.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/checkpoint.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/diarize.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/engine.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/executor.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/jobs.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/model.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/paths.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/pipeline.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/retrieval.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/runner.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/service.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/sqlite_store.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/startup.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/submission.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/timeutil.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/translate.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/whistle.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/core/whistle_assets.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/__init__.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/_cue_layout.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/_cue_text.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/_rename_excl.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/_rename_noreplace.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/docx.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/markdown.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/pdf.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/srt.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/txt.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/render/vtt.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/sources/__init__.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/sources/acquire.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/sources/detect.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/src/textflowkit/sources/scratch.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/__init__.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/ollama_stub.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_acquire.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_adapters.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_admission_ownership.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_atomic_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_atomic_resume_aba.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_batch.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_batch_item_boundary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_batch_resume_shared.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_bind.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cancellation_finalization.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_checkpoint.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_checkpoint_single_copy.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli_batch_feedback.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli_owning_recovery.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli_resume_batch.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli_stage_feedback.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cli_stdout_format.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_confined_decoder_boundary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_confined_indirect_media.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_confined_resume_boundary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_container_example.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_cue_layout.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_diarize.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_diarize_example_flags.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_diarize_token_docs.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_docx_publication_identity.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_done_output_integrity.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_empty_formats_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_engine_adapters.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_engine_cache.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_engine_faster.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_engine_quiet.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_executor.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_executor_store_failure.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_export.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_export_file_mode.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_export_preflight.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_faster_whisper_docs.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_fetch_published_fonts.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_font_notices.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_http_host_boundary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_http_trusted_proxy.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_job_progress.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_jobs.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_landing_page.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_legal_summary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_linux_no_hardlink.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_live_smoke_script.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_macos_no_hardlink.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_batch_cookie_docs.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_http_boundary.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_protocol_cleanliness.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_terminal_guidance.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_tool_descriptions.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_mcp_work_root.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_model.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_partial_output_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_partial_pdf_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_paths.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_pipeline_integrity.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_pipeline_resume_setup.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_production_browser_cookies.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_publish_ci_gate.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_publish_fonts_wiring.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_publish_manifest_wiring.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_publish_workflow_gate.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_readme_layout.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_readme_release_guard.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_release_boundaries.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_release_manifest.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_release_surfaces.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_render.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_retrieval.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_sdist_allowlist.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_service_profile.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_site_not_found.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_sources.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_stage_reuse_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_startup_recovery.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_stdio_protocol.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_submission.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_submission_validation.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_translate.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_translate_completeness.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_verify_release_versions.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_whistle_assets.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_whistle_defaults.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_whistle_engine.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_whistle_job_resume.py +0 -0
- {textflowkit-0.1.9 → textflowkit-0.1.10}/tests/test_windows_no_hardlink.py +0 -0
|
@@ -90,6 +90,6 @@ jobs:
|
|
|
90
90
|
python -m pip install "setuptools>=83" "build>=1.2"
|
|
91
91
|
python -m build --wheel
|
|
92
92
|
python -m build --wheel --outdir dist packages/textflowkit-fonts
|
|
93
|
-
- name: Smoke all
|
|
93
|
+
- name: Smoke all four installed entry points (CLI, MCP, HTTP, served UI)
|
|
94
94
|
shell: bash
|
|
95
95
|
run: python scripts/smoke_installed_wheel.py dist/textflowkit-*.whl dist/textflowkit_fonts-*.whl
|
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.1.10 — 2026-10-06
|
|
4
|
+
|
|
5
|
+
**The local browser interface ships.** The loopback-only browser workspace
|
|
6
|
+
(`textflowkit-ui`) is now part of the core package rather than source-only, so a
|
|
7
|
+
regular install of this release carries the command. Core version: 0.1.10;
|
|
8
|
+
unchanged optional fonts: 0.1.6.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Local browser interface in the core package.** `textflowkit-ui` serves a
|
|
13
|
+
point-and-click workspace on **this machine only**: it binds `127.0.0.1` (never
|
|
14
|
+
off the machine, whatever `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a
|
|
15
|
+
browser once serving. It mounts the existing developer HTTP app under `/api`
|
|
16
|
+
**unchanged** — the same routes, submission contract, and job store — and adds
|
|
17
|
+
only transport: a loopback session layer, a streamed upload, a fixed-format
|
|
18
|
+
download, in-workspace playback, and a capability endpoint. It adds **no new
|
|
19
|
+
dependency** beyond the existing `http` extra (FastAPI + uvicorn).
|
|
20
|
+
- **Durable jobs, cancel and resume in the workspace.** Jobs and checkpoints are
|
|
21
|
+
stored durably under the per-user data directory unless
|
|
22
|
+
`TEXTFLOWKIT_DB` / `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT` is set.
|
|
23
|
+
A dropped file is streamed and capped at **2 GiB** by default
|
|
24
|
+
(`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it). A running job can be
|
|
25
|
+
cancelled, and a durable job resumed.
|
|
26
|
+
- **Windows desktop shortcut.** `textflowkit-ui --create-shortcut` creates a Start
|
|
27
|
+
Menu entry that launches with `pythonw` (no console) and needs no `pywin32`.
|
|
28
|
+
Under `pythonw` a startup failure is logged under the per-user data directory
|
|
29
|
+
and shown in a message box rather than failing silently.
|
|
30
|
+
- **Controlled shutdown that drains.** **Stop server** asks this process's own
|
|
31
|
+
uvicorn server to exit through its supported `should_exit` flag; the normal
|
|
32
|
+
lifespan teardown **waits for current jobs to finish** before draining the
|
|
33
|
+
worker pool rather than interrupting them, so a running job's saved progress
|
|
34
|
+
survives and is resumable. It never sends a signal to a pid.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **Documentation surfaces.** README, user manual, install notes, developer
|
|
39
|
+
manual, architecture guide, roadmap, release checklist, site deployment, the
|
|
40
|
+
landing page, and the checked-in SVG diagrams now describe the local browser
|
|
41
|
+
interface as a shipped feature and a fifth door, and remove the earlier
|
|
42
|
+
"unreleased candidate / install from source only" wording.
|
|
43
|
+
- **No endpoint-breaking change.** The CLI, Python API, MCP server, and JSON HTTP
|
|
44
|
+
adapter are unchanged in this release; the optional engines, diarization, and
|
|
45
|
+
translation keep their documented dependency requirements.
|
|
46
|
+
|
|
47
|
+
### Verification boundary
|
|
48
|
+
|
|
49
|
+
Automated coverage runs the UI's routes, packaging, ownership/shutdown, and
|
|
50
|
+
security boundaries. A **native Windows** machine, in a real browser, proved the
|
|
51
|
+
workspace end to end (drop a file, submit, download, stop). That is one platform's
|
|
52
|
+
browser proof, **not** a live Linux or macOS UI run — those platforms are covered
|
|
53
|
+
by automated tests only, and cross-platform CI (Windows/Linux/macOS) is a separate
|
|
54
|
+
step run by the coordinator. **No live optional-backend (translation or
|
|
55
|
+
diarization) run through the UI is claimed.** The engine and packaging changes of
|
|
56
|
+
v0.1.9 are unchanged by this release.
|
|
57
|
+
|
|
3
58
|
## v0.1.9 — 2026-10-05
|
|
4
59
|
|
|
5
60
|
**Whistle is now the default engine.** This release ships the Whistle integration
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: textflowkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out.
|
|
5
5
|
Project-URL: Homepage, https://www.textflowkit.org/
|
|
6
6
|
Project-URL: Issues, https://github.com/scottconverse/textflowkit/issues
|
|
@@ -67,7 +67,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
|
|
|
67
67
|
[User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
|
|
68
68
|
[Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
|
|
69
69
|
|
|
70
|
-
**Current release: [v0.1.
|
|
70
|
+
**Current release: [v0.1.10](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.10).**
|
|
71
71
|
|
|
72
72
|
The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
|
|
73
73
|
Pages. GitHub remains the source and CI host; the website does not run the
|
|
@@ -128,10 +128,11 @@ DOCX and PDF are available through the optional `export` extra.
|
|
|
128
128
|
|
|
129
129
|
## Architecture
|
|
130
130
|
|
|
131
|
-

|
|
132
132
|
|
|
133
|
-
The design principle is **one
|
|
134
|
-
the core; the interfaces are thin
|
|
133
|
+
The design principle is **one shared core, thin doors**. Everything of substance
|
|
134
|
+
lives in the core; the interfaces are thin — the local browser UI is a fifth door
|
|
135
|
+
that adds only transport over the existing HTTP app and shared job store.
|
|
135
136
|
|
|
136
137
|
| Layer | Path | Responsibility |
|
|
137
138
|
|---|---|---|
|
|
@@ -141,6 +142,7 @@ the core; the interfaces are thin.
|
|
|
141
142
|
| **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
|
|
142
143
|
| **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
|
|
143
144
|
| **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
|
|
145
|
+
| **Local UI** | `src/textflowkit/ui` | Loopback-only browser workspace over the same HTTP app and job store |
|
|
144
146
|
|
|
145
147
|
Because the core owns the pipeline, adding a door is cheap — and adding a platform
|
|
146
148
|
means writing one source adapter, not another tool.
|
|
@@ -294,7 +296,7 @@ textflowkit export ./transcript.json --format vtt
|
|
|
294
296
|
needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
|
|
295
297
|
torch; `openai-whisper` is an explicitly selectable opt-in engine.
|
|
296
298
|
|
|
297
|
-
, each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.10/docs/assets/whistle-resume.svg)
|
|
298
300
|
|
|
299
301
|
- **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
|
|
300
302
|
model on first use. Naming a GPU device is refused rather than silently
|
|
@@ -397,6 +399,66 @@ contract checks cover it, and running an actual build is deliberately out of
|
|
|
397
399
|
scope here, so no image is claimed to build or start. See
|
|
398
400
|
[the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
|
|
399
401
|
|
|
402
|
+
## Local browser interface
|
|
403
|
+
|
|
404
|
+
For point-and-click use there is a local browser workspace that runs on **this
|
|
405
|
+
machine only**, reuses the same pipeline and job store, and adds no dependency of
|
|
406
|
+
its own beyond the `http` extra. It ships with the core package as of v0.1.10:
|
|
407
|
+
a regular install of the current release carries it.
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
python -m pip install 'textflowkit[http,export]==0.1.10'
|
|
411
|
+
textflowkit-ui
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
On Windows you can create a desktop shortcut when you want one:
|
|
415
|
+
|
|
416
|
+
```powershell
|
|
417
|
+
textflowkit-ui --create-shortcut # Start Menu entry (pythonw, no console)
|
|
418
|
+
textflowkit-ui --create-shortcut --shortcut-dir 'C:\path\to\folder'
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The shortcut is created only on request and needs no `pywin32`. Under `pythonw`
|
|
422
|
+
there is no console, so a startup failure is written to a log file under the
|
|
423
|
+
per-user data directory and shown in a message box rather than failing silently.
|
|
424
|
+
The normal operator path is to launch from that shortcut; the console command
|
|
425
|
+
above is equivalent.
|
|
426
|
+
|
|
427
|
+
**This is a local app, not a public website.** The [landing page](https://www.textflowkit.org/)
|
|
428
|
+
is static documentation served by Cloudflare Pages; it does **not** run the
|
|
429
|
+
transcription engine and offers no upload form. The browser workspace exists only
|
|
430
|
+
on the machine that installed it.
|
|
431
|
+
|
|
432
|
+
- **Loopback only.** It binds `127.0.0.1` (never off the machine, whatever
|
|
433
|
+
`TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a browser once it is serving.
|
|
434
|
+
- **One owner per database.** A second launch against the same database opens the
|
|
435
|
+
UI already running rather than starting a competing server. **Stop server** in
|
|
436
|
+
the workspace shuts the local server down without a terminal, waiting for the
|
|
437
|
+
current jobs to finish rather than interrupting them. Shutdown **drains** — it
|
|
438
|
+
lets in-flight jobs complete; it does **not** auto-cancel them. Cancel a job
|
|
439
|
+
first to stop it early.
|
|
440
|
+
- **Durable jobs with cancel and resume.** Jobs and checkpoints are stored
|
|
441
|
+
durably under the per-user data directory unless you set `TEXTFLOWKIT_DB` /
|
|
442
|
+
`TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT`. A dropped file is capped
|
|
443
|
+
at **2 GiB** by default (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it).
|
|
444
|
+
In-workspace playback uses the browser's native player on the **currently
|
|
445
|
+
selected** media, so it depends on what that browser can decode; it is a
|
|
446
|
+
convenience preview, not a format guarantee.
|
|
447
|
+
- **No UI analytics.** The page loads no CDN asset and no analytics, and the
|
|
448
|
+
session token lives only in the page — never in a URL, cookie, or log line.
|
|
449
|
+
- **What uses the network.** The page itself loads nothing remote. A **URL
|
|
450
|
+
source** is downloaded from the internet; an engine's **model** may be
|
|
451
|
+
downloaded on first use; and **remote or cloud translation** (an advanced
|
|
452
|
+
option) sends transcript text to that backend. With local media and a local
|
|
453
|
+
translation model, no transcript leaves the machine.
|
|
454
|
+
- `--port`, `--no-browser`, and `--version` are available.
|
|
455
|
+
|
|
456
|
+
The other doors are unchanged: the CLI, Python API, MCP, and HTTP adapters keep
|
|
457
|
+
their existing behavior, and the optional engines, diarization, and translation
|
|
458
|
+
retain their documented dependency requirements. See the
|
|
459
|
+
[user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md#7-local-browser-interface)
|
|
460
|
+
and [architecture guide](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md#one-owner-per-database).
|
|
461
|
+
|
|
400
462
|
## Durable, bounded, cancellable
|
|
401
463
|
|
|
402
464
|
```bash
|
|
@@ -411,23 +473,38 @@ boundary. See [docs/adapters.md](https://github.com/scottconverse/textflowkit/bl
|
|
|
411
473
|
|
|
412
474
|
The MCP and HTTP adapters are **job-based**: submission returns a job id
|
|
413
475
|
immediately and clients poll for completion. The CLI uses the same core but
|
|
414
|
-
waits for the result. This lets AI harnesses, software products, and
|
|
415
|
-
|
|
476
|
+
waits for the result. This lets AI harnesses, software products, and the local
|
|
477
|
+
browser interface share the job contract without blocking a request.
|
|
416
478
|
|
|
417
479
|
## Status
|
|
418
480
|
|
|
419
|
-
**v0.1.
|
|
420
|
-
coverage. This release
|
|
421
|
-
|
|
422
|
-
|
|
481
|
+
**v0.1.10 release.** Core, CLI, MCP, HTTP, and the local browser interface have
|
|
482
|
+
automated coverage. This release **ships the local browser interface**
|
|
483
|
+
(`textflowkit-ui`) in the core package: a loopback-only workspace that reuses the
|
|
484
|
+
same pipeline, submission contract, and job store, with a durable store, cancel
|
|
485
|
+
and resume, and a Windows desktop shortcut (`textflowkit-ui --create-shortcut`).
|
|
486
|
+
It adds **no new dependency** beyond the existing `http` extra, so a regular
|
|
487
|
+
`pip install 'textflowkit[http,export]==0.1.10'` carries it. The engine, CLI,
|
|
488
|
+
Python, MCP, and HTTP surfaces are unchanged and there is **no endpoint-breaking
|
|
489
|
+
change**. It builds on v0.1.9, which made **Whistle the default engine** — a
|
|
490
|
+
native CPU-only transcriber that needs no PyTorch — and moved `openai-whisper` to
|
|
491
|
+
the optional `whisper` extra, selected explicitly with `--engine whisper` or
|
|
423
492
|
`engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
|
|
424
493
|
`transcribe()` calls still decode. Whistle refuses an unsupported platform,
|
|
425
|
-
language, or GPU request instead of silently falling back
|
|
494
|
+
language, or GPU request instead of silently falling back; its engine's
|
|
426
495
|
verification boundary is in [Whistle (default engine)](#whistle-default-engine)
|
|
427
|
-
above.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
496
|
+
above.
|
|
497
|
+
|
|
498
|
+
**Verification limit.** The local browser interface is covered by an automated
|
|
499
|
+
suite, and the UI's behavior was proved on a **native Windows** machine in a real
|
|
500
|
+
browser. No live optional-backend (translation/diarization) run through the UI is
|
|
501
|
+
claimed, and **no live Linux or macOS UI run was performed here** — those
|
|
502
|
+
platforms are covered by automated tests only, and CI across Windows, Linux, and
|
|
503
|
+
macOS is the coordinator's separate step. Release publication uses the tag
|
|
504
|
+
workflow, which requires successful exact-commit main CI before PyPI uploads and
|
|
505
|
+
creates the public GitHub release only afterward. Check the linked release for
|
|
506
|
+
artifacts and workflow status; local source verification is not a fresh
|
|
507
|
+
PyPI-install or individual-harness receipt.
|
|
431
508
|
|
|
432
509
|
The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
|
|
433
510
|
(findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
|
|
@@ -439,8 +516,8 @@ that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP an
|
|
|
439
516
|
MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
|
|
440
517
|
were independently checked through fresh-process CLI restart probes, real SQLite
|
|
441
518
|
setup-failure tests, and the full Windows test suite. Those checks are not new
|
|
442
|
-
individual-harness receipts. That is the previous
|
|
443
|
-
for v0.1.
|
|
519
|
+
individual-harness receipts. That is the previous releases' record, not evidence
|
|
520
|
+
for v0.1.10.
|
|
444
521
|
|
|
445
522
|
The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
|
|
446
523
|
for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
|
|
@@ -459,7 +536,7 @@ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
|
|
|
459
536
|
[fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
|
|
460
537
|
live — that is the v0.1.6 release's evidence, not a receipt for a later release.
|
|
461
538
|
Everything from here to the end of this section is the v0.1.6 release's
|
|
462
|
-
historical record as published: it is not re-verified for v0.1.
|
|
539
|
+
historical record as published: it is not re-verified for v0.1.10, whose separate release evidence is not supplied by these historical paragraphs.
|
|
463
540
|
Merged-main CI passed 16/16 on the tagged commit; the published wheel and
|
|
464
541
|
sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
|
|
465
542
|
Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
|
|
@@ -8,7 +8,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
|
|
|
8
8
|
[User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
|
|
9
9
|
[Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
|
|
10
10
|
|
|
11
|
-
**Current release: [v0.1.
|
|
11
|
+
**Current release: [v0.1.10](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.10).**
|
|
12
12
|
|
|
13
13
|
The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
|
|
14
14
|
Pages. GitHub remains the source and CI host; the website does not run the
|
|
@@ -69,10 +69,11 @@ DOCX and PDF are available through the optional `export` extra.
|
|
|
69
69
|
|
|
70
70
|
## Architecture
|
|
71
71
|
|
|
72
|
-

|
|
73
73
|
|
|
74
|
-
The design principle is **one
|
|
75
|
-
the core; the interfaces are thin
|
|
74
|
+
The design principle is **one shared core, thin doors**. Everything of substance
|
|
75
|
+
lives in the core; the interfaces are thin — the local browser UI is a fifth door
|
|
76
|
+
that adds only transport over the existing HTTP app and shared job store.
|
|
76
77
|
|
|
77
78
|
| Layer | Path | Responsibility |
|
|
78
79
|
|---|---|---|
|
|
@@ -82,6 +83,7 @@ the core; the interfaces are thin.
|
|
|
82
83
|
| **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
|
|
83
84
|
| **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
|
|
84
85
|
| **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
|
|
86
|
+
| **Local UI** | `src/textflowkit/ui` | Loopback-only browser workspace over the same HTTP app and job store |
|
|
85
87
|
|
|
86
88
|
Because the core owns the pipeline, adding a door is cheap — and adding a platform
|
|
87
89
|
means writing one source adapter, not another tool.
|
|
@@ -235,7 +237,7 @@ textflowkit export ./transcript.json --format vtt
|
|
|
235
237
|
needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
|
|
236
238
|
torch; `openai-whisper` is an explicitly selectable opt-in engine.
|
|
237
239
|
|
|
238
|
-
, each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.10/docs/assets/whistle-resume.svg)
|
|
239
241
|
|
|
240
242
|
- **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
|
|
241
243
|
model on first use. Naming a GPU device is refused rather than silently
|
|
@@ -338,6 +340,66 @@ contract checks cover it, and running an actual build is deliberately out of
|
|
|
338
340
|
scope here, so no image is claimed to build or start. See
|
|
339
341
|
[the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
|
|
340
342
|
|
|
343
|
+
## Local browser interface
|
|
344
|
+
|
|
345
|
+
For point-and-click use there is a local browser workspace that runs on **this
|
|
346
|
+
machine only**, reuses the same pipeline and job store, and adds no dependency of
|
|
347
|
+
its own beyond the `http` extra. It ships with the core package as of v0.1.10:
|
|
348
|
+
a regular install of the current release carries it.
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
python -m pip install 'textflowkit[http,export]==0.1.10'
|
|
352
|
+
textflowkit-ui
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
On Windows you can create a desktop shortcut when you want one:
|
|
356
|
+
|
|
357
|
+
```powershell
|
|
358
|
+
textflowkit-ui --create-shortcut # Start Menu entry (pythonw, no console)
|
|
359
|
+
textflowkit-ui --create-shortcut --shortcut-dir 'C:\path\to\folder'
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The shortcut is created only on request and needs no `pywin32`. Under `pythonw`
|
|
363
|
+
there is no console, so a startup failure is written to a log file under the
|
|
364
|
+
per-user data directory and shown in a message box rather than failing silently.
|
|
365
|
+
The normal operator path is to launch from that shortcut; the console command
|
|
366
|
+
above is equivalent.
|
|
367
|
+
|
|
368
|
+
**This is a local app, not a public website.** The [landing page](https://www.textflowkit.org/)
|
|
369
|
+
is static documentation served by Cloudflare Pages; it does **not** run the
|
|
370
|
+
transcription engine and offers no upload form. The browser workspace exists only
|
|
371
|
+
on the machine that installed it.
|
|
372
|
+
|
|
373
|
+
- **Loopback only.** It binds `127.0.0.1` (never off the machine, whatever
|
|
374
|
+
`TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a browser once it is serving.
|
|
375
|
+
- **One owner per database.** A second launch against the same database opens the
|
|
376
|
+
UI already running rather than starting a competing server. **Stop server** in
|
|
377
|
+
the workspace shuts the local server down without a terminal, waiting for the
|
|
378
|
+
current jobs to finish rather than interrupting them. Shutdown **drains** — it
|
|
379
|
+
lets in-flight jobs complete; it does **not** auto-cancel them. Cancel a job
|
|
380
|
+
first to stop it early.
|
|
381
|
+
- **Durable jobs with cancel and resume.** Jobs and checkpoints are stored
|
|
382
|
+
durably under the per-user data directory unless you set `TEXTFLOWKIT_DB` /
|
|
383
|
+
`TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT`. A dropped file is capped
|
|
384
|
+
at **2 GiB** by default (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it).
|
|
385
|
+
In-workspace playback uses the browser's native player on the **currently
|
|
386
|
+
selected** media, so it depends on what that browser can decode; it is a
|
|
387
|
+
convenience preview, not a format guarantee.
|
|
388
|
+
- **No UI analytics.** The page loads no CDN asset and no analytics, and the
|
|
389
|
+
session token lives only in the page — never in a URL, cookie, or log line.
|
|
390
|
+
- **What uses the network.** The page itself loads nothing remote. A **URL
|
|
391
|
+
source** is downloaded from the internet; an engine's **model** may be
|
|
392
|
+
downloaded on first use; and **remote or cloud translation** (an advanced
|
|
393
|
+
option) sends transcript text to that backend. With local media and a local
|
|
394
|
+
translation model, no transcript leaves the machine.
|
|
395
|
+
- `--port`, `--no-browser`, and `--version` are available.
|
|
396
|
+
|
|
397
|
+
The other doors are unchanged: the CLI, Python API, MCP, and HTTP adapters keep
|
|
398
|
+
their existing behavior, and the optional engines, diarization, and translation
|
|
399
|
+
retain their documented dependency requirements. See the
|
|
400
|
+
[user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md#7-local-browser-interface)
|
|
401
|
+
and [architecture guide](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md#one-owner-per-database).
|
|
402
|
+
|
|
341
403
|
## Durable, bounded, cancellable
|
|
342
404
|
|
|
343
405
|
```bash
|
|
@@ -352,23 +414,38 @@ boundary. See [docs/adapters.md](https://github.com/scottconverse/textflowkit/bl
|
|
|
352
414
|
|
|
353
415
|
The MCP and HTTP adapters are **job-based**: submission returns a job id
|
|
354
416
|
immediately and clients poll for completion. The CLI uses the same core but
|
|
355
|
-
waits for the result. This lets AI harnesses, software products, and
|
|
356
|
-
|
|
417
|
+
waits for the result. This lets AI harnesses, software products, and the local
|
|
418
|
+
browser interface share the job contract without blocking a request.
|
|
357
419
|
|
|
358
420
|
## Status
|
|
359
421
|
|
|
360
|
-
**v0.1.
|
|
361
|
-
coverage. This release
|
|
362
|
-
|
|
363
|
-
|
|
422
|
+
**v0.1.10 release.** Core, CLI, MCP, HTTP, and the local browser interface have
|
|
423
|
+
automated coverage. This release **ships the local browser interface**
|
|
424
|
+
(`textflowkit-ui`) in the core package: a loopback-only workspace that reuses the
|
|
425
|
+
same pipeline, submission contract, and job store, with a durable store, cancel
|
|
426
|
+
and resume, and a Windows desktop shortcut (`textflowkit-ui --create-shortcut`).
|
|
427
|
+
It adds **no new dependency** beyond the existing `http` extra, so a regular
|
|
428
|
+
`pip install 'textflowkit[http,export]==0.1.10'` carries it. The engine, CLI,
|
|
429
|
+
Python, MCP, and HTTP surfaces are unchanged and there is **no endpoint-breaking
|
|
430
|
+
change**. It builds on v0.1.9, which made **Whistle the default engine** — a
|
|
431
|
+
native CPU-only transcriber that needs no PyTorch — and moved `openai-whisper` to
|
|
432
|
+
the optional `whisper` extra, selected explicitly with `--engine whisper` or
|
|
364
433
|
`engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
|
|
365
434
|
`transcribe()` calls still decode. Whistle refuses an unsupported platform,
|
|
366
|
-
language, or GPU request instead of silently falling back
|
|
435
|
+
language, or GPU request instead of silently falling back; its engine's
|
|
367
436
|
verification boundary is in [Whistle (default engine)](#whistle-default-engine)
|
|
368
|
-
above.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
437
|
+
above.
|
|
438
|
+
|
|
439
|
+
**Verification limit.** The local browser interface is covered by an automated
|
|
440
|
+
suite, and the UI's behavior was proved on a **native Windows** machine in a real
|
|
441
|
+
browser. No live optional-backend (translation/diarization) run through the UI is
|
|
442
|
+
claimed, and **no live Linux or macOS UI run was performed here** — those
|
|
443
|
+
platforms are covered by automated tests only, and CI across Windows, Linux, and
|
|
444
|
+
macOS is the coordinator's separate step. Release publication uses the tag
|
|
445
|
+
workflow, which requires successful exact-commit main CI before PyPI uploads and
|
|
446
|
+
creates the public GitHub release only afterward. Check the linked release for
|
|
447
|
+
artifacts and workflow status; local source verification is not a fresh
|
|
448
|
+
PyPI-install or individual-harness receipt.
|
|
372
449
|
|
|
373
450
|
The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
|
|
374
451
|
(findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
|
|
@@ -380,8 +457,8 @@ that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP an
|
|
|
380
457
|
MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
|
|
381
458
|
were independently checked through fresh-process CLI restart probes, real SQLite
|
|
382
459
|
setup-failure tests, and the full Windows test suite. Those checks are not new
|
|
383
|
-
individual-harness receipts. That is the previous
|
|
384
|
-
for v0.1.
|
|
460
|
+
individual-harness receipts. That is the previous releases' record, not evidence
|
|
461
|
+
for v0.1.10.
|
|
385
462
|
|
|
386
463
|
The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
|
|
387
464
|
for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
|
|
@@ -400,7 +477,7 @@ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
|
|
|
400
477
|
[fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
|
|
401
478
|
live — that is the v0.1.6 release's evidence, not a receipt for a later release.
|
|
402
479
|
Everything from here to the end of this section is the v0.1.6 release's
|
|
403
|
-
historical record as published: it is not re-verified for v0.1.
|
|
480
|
+
historical record as published: it is not re-verified for v0.1.10, whose separate release evidence is not supplied by these historical paragraphs.
|
|
404
481
|
Merged-main CI passed 16/16 on the tagged commit; the published wheel and
|
|
405
482
|
sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
|
|
406
483
|
Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Developer and integration manual
|
|
2
2
|
|
|
3
|
-
Current release: [v0.1.
|
|
3
|
+
Current release: [v0.1.10](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.10).
|
|
4
4
|
Start with the [user manual](user-manual.md) for everyday use. Harness versions
|
|
5
5
|
in the compatibility table below identify **historical test sessions**, not
|
|
6
6
|
the version of TextFlowKit currently published.
|
|
7
7
|
|
|
8
8
|
textflowkit has one core and several thin doors. Nothing is duplicated between
|
|
9
|
-
them: the CLI, the MCP server,
|
|
10
|
-
`textflowkit.core.submission` and share one
|
|
9
|
+
them: the CLI, the MCP server, the HTTP API, and the local browser
|
|
10
|
+
interface (`textflowkit-ui`) all call `textflowkit.core.submission` and share one
|
|
11
|
+
job model.
|
|
11
12
|
|
|
12
|
-

|
|
13
14
|
|
|
14
15
|
```
|
|
15
16
|
┌──────────────────────┐
|
|
@@ -505,6 +506,77 @@ agree. So if you do leave it on, put it behind a proxy that appends its own
|
|
|
505
506
|
observation rather than forwarding the caller's header, and keep
|
|
506
507
|
`FORWARDED_ALLOW_IPS` narrow.
|
|
507
508
|
|
|
509
|
+
## Local browser interface (`textflowkit-ui`)
|
|
510
|
+
|
|
511
|
+
`textflowkit-ui` is a fourth thin door beside the CLI, MCP, and HTTP entry
|
|
512
|
+
points. It mounts the developer HTTP app (`adapters.http_server`) under `/api`
|
|
513
|
+
**unchanged** - the same routes, the same submission contract, the same job
|
|
514
|
+
store - and adds only transport: a loopback-only browser session layer, a
|
|
515
|
+
streamed upload, a fixed-format download, and a capability endpoint. It
|
|
516
|
+
**ships in the core package as of v0.1.10** (it needs the `http` extra, which
|
|
517
|
+
supplies FastAPI and uvicorn, and adds no new dependency). It has no public site
|
|
518
|
+
of its own: it is a local app, not a hosted service.
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
python -m pip install 'textflowkit[http,export]==0.1.10'
|
|
522
|
+
textflowkit-ui # serve on 127.0.0.1 and open a browser
|
|
523
|
+
textflowkit-ui --no-browser --port 8901
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+

|
|
527
|
+
|
|
528
|
+
Because it is a browser door and not a product, its rules are **stricter** than
|
|
529
|
+
the developer HTTP app's:
|
|
530
|
+
|
|
531
|
+
- **Loopback only, always.** It binds `127.0.0.1` and refuses any non-loopback
|
|
532
|
+
host, and it does **not** honour `TEXTFLOWKIT_ALLOW_REMOTE` - that opt-in widens
|
|
533
|
+
the developer API, and an unauthenticated browser interface must not be
|
|
534
|
+
reachable off the machine.
|
|
535
|
+
- **A session capability on every mutator.** The shell embeds a per-process
|
|
536
|
+
random token in a `<meta>` element; every POST carries it in
|
|
537
|
+
`x-textflowkit-ui-capability`. A request must also pass a loopback host, a
|
|
538
|
+
provably-loopback peer, and an `Origin` that is *exactly* this UI's own origin.
|
|
539
|
+
The token never appears in a URL, a cookie, or a log line.
|
|
540
|
+
- **No external asset, no telemetry.** The shell and its CSS/JS are served from
|
|
541
|
+
the package; the page loads no CDN script and no analytics. (`/ui/capabilities`
|
|
542
|
+
reports what the install can do without loading a model or downloading
|
|
543
|
+
anything.) Network *is* used for a URL source, an engine's first-use model
|
|
544
|
+
download, and a remote/cloud translation backend - see the translation caveat
|
|
545
|
+
[above](#translation): a `:cloud` model or a remote `TEXTFLOWKIT_OLLAMA_HOST`
|
|
546
|
+
sends transcript text to that host. Local media and a local translation model
|
|
547
|
+
keep transcripts on the machine.
|
|
548
|
+
|
|
549
|
+
### One owner per database
|
|
550
|
+
|
|
551
|
+
The UI runs startup recovery and the worker pool, so a store is owned by one UI
|
|
552
|
+
process. It takes an **operating-system file lock** (`msvcrt` on Windows, `flock`
|
|
553
|
+
on POSIX) on a persistent sibling of the database, keyed by the *resolved* path,
|
|
554
|
+
*before* recovery runs. The kernel holds the lock for the live holder and releases
|
|
555
|
+
it on exit **or crash**, so a killed run never blocks the next launch. A second
|
|
556
|
+
launch discovers the running instance's loopback URL from a small record beside
|
|
557
|
+
the lock and opens that instead of starting a competing server. Ownership is
|
|
558
|
+
proved by the held lock plus a matching record - never by signalling or inspecting
|
|
559
|
+
a pid. The same rule as every door still applies: **do not point two processes at
|
|
560
|
+
one database file.** See the [architecture guide](architecture.md#one-owner-per-database).
|
|
561
|
+
|
|
562
|
+
### Uploads and downloads
|
|
563
|
+
|
|
564
|
+
A dropped file is streamed as the **raw request body** to a fresh, server-named
|
|
565
|
+
file under the per-user work directory (default cap 2 GiB,
|
|
566
|
+
`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it); the size is capped both by the
|
|
567
|
+
declared length and while streaming, and a refused, aborted, or oversized upload
|
|
568
|
+
leaves no partial file. The staged path is generated by the server, never taken
|
|
569
|
+
from the request, and a `mode: "upload"` submission names only an `upload_id`
|
|
570
|
+
that is resolved back inside the uploads directory. Downloads come from a **fixed
|
|
571
|
+
format allowlist** (TXT, Markdown, SRT, VTT, JSON, DOCX, PDF) rendered through the
|
|
572
|
+
same `render` layer the other doors use; the filename is built from the job id,
|
|
573
|
+
never from caller input.
|
|
574
|
+
|
|
575
|
+
The UI adds **no new storage variable**: it fills in `TEXTFLOWKIT_DB`,
|
|
576
|
+
`TEXTFLOWKIT_WORK_ROOT`, and `TEXTFLOWKIT_OUTPUT_ROOT` with per-user defaults
|
|
577
|
+
(`%LOCALAPPDATA%\TextFlowKit\ui` on Windows) only when they are unset, so an
|
|
578
|
+
explicit environment override is honoured unchanged.
|
|
579
|
+
|
|
508
580
|
## Durable job state
|
|
509
581
|
|
|
510
582
|
By default jobs live in memory and are lost when the process exits. Set
|
|
@@ -7,18 +7,60 @@ current source paths noted throughout.
|
|
|
7
7
|
|
|
8
8
|
## One core, several doors
|
|
9
9
|
|
|
10
|
-
TextFlowKit is a self-hosted media transcription library with a CLI
|
|
11
|
-
|
|
10
|
+
TextFlowKit is a self-hosted media transcription library with a CLI, two adapters
|
|
11
|
+
(MCP and a JSON HTTP API), and a local browser interface. Native Windows is the primary local target,
|
|
12
12
|
including AMD ROCm through PyTorch for the explicit `whisper` engine; no WSL layer
|
|
13
13
|
is involved. Linux and macOS are additional targets. The published website is
|
|
14
14
|
static documentation and never runs the pipeline.
|
|
15
15
|
|
|
16
|
-

|
|
17
17
|
|
|
18
18
|
As of v0.1.9 the default engine is **Whistle**, a CPU-only native CLI that needs
|
|
19
19
|
no torch and downloads one pinned model on first use. `openai-whisper` is an
|
|
20
20
|
explicit opt-in engine (`--engine whisper`), not the default.
|
|
21
21
|
|
|
22
|
+
A **local browser interface** (`textflowkit-ui`) is a further door alongside the
|
|
23
|
+
CLI, MCP, and HTTP entry points. It mounts the existing developer HTTP app under
|
|
24
|
+
`/api` unchanged and adds only transport: a loopback-only browser session layer,
|
|
25
|
+
a streamed upload, and a capability endpoint. It **ships in the core package as
|
|
26
|
+
of v0.1.10** and adds no new dependency beyond the `http` extra. It is a local
|
|
27
|
+
app: the published website is static documentation and never runs the pipeline.
|
|
28
|
+
|
|
29
|
+
### One owner per database
|
|
30
|
+
|
|
31
|
+
The durable SQLite store is owned by exactly one process. `textflowkit-ui`
|
|
32
|
+
enforces that with an exclusive owner lock (`ui/ownership.py`) keyed by the
|
|
33
|
+
**resolved** database path and taken *before* startup recovery runs. The lock is a
|
|
34
|
+
genuine operating-system file lock (`msvcrt` on Windows, `flock` on POSIX) on a
|
|
35
|
+
small sibling file that is created once and **never unlinked**, so there is no
|
|
36
|
+
unlink/recreate window in which two processes could lock different inodes at one
|
|
37
|
+
path. The kernel holds the lock for the live holder and releases it on exit *or
|
|
38
|
+
crash*, so a run that was killed does not block the next launch. A second launch
|
|
39
|
+
against a live owner does not start a competing server: it discovers the running
|
|
40
|
+
instance's loopback URL from a small owner record written beside the lock and
|
|
41
|
+
opens that instead. Ownership is proved by the held lock plus a matching owner
|
|
42
|
+
record — never by signalling or inspecting a pid. A leftover URL record with no
|
|
43
|
+
held lock is not treated as a live instance. Nothing here signals, stops, or
|
|
44
|
+
cleans up any other process — the documented rule is simply **do not point two
|
|
45
|
+
processes at one database file.**
|
|
46
|
+
|
|
47
|
+
Readiness is a property of the process, not a probe: the launcher reserves the
|
|
48
|
+
listening socket itself and drives uvicorn with `Server.serve(sockets=[...])`, so
|
|
49
|
+
the browser is opened only after *our* server is serving, and an unrelated
|
|
50
|
+
service holding the preferred port number is passed over for a genuinely free
|
|
51
|
+
one.
|
|
52
|
+
|
|
53
|
+
A **controlled shutdown** (`POST /ui/shutdown`, gated like every mutator by the
|
|
54
|
+
loopback origin gate and the session capability header, and confirmed in the
|
|
55
|
+
browser) asks this process's own uvicorn server to exit through its supported
|
|
56
|
+
`should_exit` flag. The server then runs its normal lifespan teardown, which
|
|
57
|
+
waits for the current jobs to finish before draining the worker pool rather than
|
|
58
|
+
interrupting them, so a running job's saved progress survives and is resumable.
|
|
59
|
+
It does not cancel an in-flight job; cancel it first for that. It never sends a
|
|
60
|
+
signal to a pid. On a console
|
|
61
|
+
(`pythonw`) launch the launcher keeps its own log file and reports a startup
|
|
62
|
+
failure in a message box, so a shortcut that fails to start is not a silent exit.
|
|
63
|
+
|
|
22
64
|
One shared submission contract (`core/submission.py`) owns job creation and
|
|
23
65
|
resume for every door. The CLI runs the pipeline synchronously; MCP and HTTP
|
|
24
66
|
enqueue work and return a job handle. The public Python `transcribe()` calls the
|
|
@@ -30,6 +72,7 @@ flowchart LR
|
|
|
30
72
|
CLI[CLI single and batch] --> SUB[core.submission]
|
|
31
73
|
MCP[MCP stdio or Streamable HTTP] --> SUB
|
|
32
74
|
HTTP[JSON HTTP adapter] --> SUB
|
|
75
|
+
UI[Local browser UI over /api] --> SUB
|
|
33
76
|
SUB --> STORE[Memory store or SQLite WAL]
|
|
34
77
|
SUB --> EXEC[In-process bounded executor]
|
|
35
78
|
SUB --> SYNC[Synchronous inline run]
|