flowjet 2.0.3__tar.gz → 2.0.4__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.

Potentially problematic release.


This version of flowjet might be problematic. Click here for more details.

Files changed (169) hide show
  1. {flowjet-2.0.3 → flowjet-2.0.4}/CHANGELOG.md +7 -0
  2. {flowjet-2.0.3 → flowjet-2.0.4}/PKG-INFO +2 -2
  3. {flowjet-2.0.3 → flowjet-2.0.4}/pyproject.toml +2 -2
  4. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/agent.py +23 -70
  5. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/cli.py +135 -4
  6. flowjet-2.0.4/src/flowjet/core/home.py +127 -0
  7. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/__main__.py +9 -0
  8. {flowjet-2.0.3 → flowjet-2.0.4}/.gitignore +0 -0
  9. {flowjet-2.0.3 → flowjet-2.0.4}/LICENSE +0 -0
  10. {flowjet-2.0.3 → flowjet-2.0.4}/README.md +0 -0
  11. {flowjet-2.0.3 → flowjet-2.0.4}/config/nano.yml +0 -0
  12. {flowjet-2.0.3 → flowjet-2.0.4}/deploy/README.md +0 -0
  13. {flowjet-2.0.3 → flowjet-2.0.4}/deploy/nano.yml +0 -0
  14. {flowjet-2.0.3 → flowjet-2.0.4}/docs/completion-design.md +0 -0
  15. {flowjet-2.0.3 → flowjet-2.0.4}/docs/drafts/README.md +0 -0
  16. {flowjet-2.0.3 → flowjet-2.0.4}/docs/drafts/api-spec.md +0 -0
  17. {flowjet-2.0.3 → flowjet-2.0.4}/docs/impl/IG-001-openai-compatible-server.md +0 -0
  18. {flowjet-2.0.3 → flowjet-2.0.4}/docs/impl/IG-002-isolated-thread-pool-runtime.md +0 -0
  19. {flowjet-2.0.3 → flowjet-2.0.4}/docs/impl/IG-003-production-isolation-hardening.md +0 -0
  20. {flowjet-2.0.3 → flowjet-2.0.4}/docs/impl/README.md +0 -0
  21. {flowjet-2.0.3 → flowjet-2.0.4}/docs/impl/TEST-COVERAGE-MATRIX.md +0 -0
  22. {flowjet-2.0.3 → flowjet-2.0.4}/docs/naming.md +0 -0
  23. {flowjet-2.0.3 → flowjet-2.0.4}/docs/server.md +0 -0
  24. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/RFC-001-openai-compatible-api.md +0 -0
  25. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/RFC-002-isolated-thread-pool-runtime.md +0 -0
  26. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/RFC-003-production-isolation-hardening.md +0 -0
  27. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/RFC-004-unified-package.md +0 -0
  28. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/rfc-history.md +0 -0
  29. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/rfc-index.md +0 -0
  30. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/rfc-namings.md +0 -0
  31. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/templates/architecture-design.md +0 -0
  32. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/templates/conceptual-design.md +0 -0
  33. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/templates/impl-interface-design.md +0 -0
  34. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/templates/rfc-standard.md +0 -0
  35. {flowjet-2.0.3 → flowjet-2.0.4}/docs/specs/templates/rfc-template.md +0 -0
  36. {flowjet-2.0.3 → flowjet-2.0.4}/docs/upgrade.md +0 -0
  37. {flowjet-2.0.3 → flowjet-2.0.4}/examples/README.md +0 -0
  38. {flowjet-2.0.3 → flowjet-2.0.4}/nano.yml +0 -0
  39. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/__init__.py +0 -0
  40. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/__main__.py +0 -0
  41. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/README.md +0 -0
  42. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/gws-drive/SKILL.md +0 -0
  43. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/gws-drive-upload/SKILL.md +0 -0
  44. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/.env.example +0 -0
  45. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/README.md +0 -0
  46. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/SKILL.md +0 -0
  47. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/assets/background_library.html +0 -0
  48. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/assets/showcase_template.html +0 -0
  49. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/references/background_styles.md +0 -0
  50. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/references/design_patterns.md +0 -0
  51. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/references/webgl_backgrounds.md +0 -0
  52. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/requirements.txt +0 -0
  53. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/scripts/generate_showcase.py +0 -0
  54. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/logo-generator-skill/scripts/svg_to_png.py +0 -0
  55. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/SKILL.md +0 -0
  56. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/brainstorming/spec-document-reviewer-prompt.md +0 -0
  57. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/coding-plan-template.md +0 -0
  58. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/impl-guide-template.md +0 -0
  59. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/review/pr-review-template.md +0 -0
  60. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/review/review-checklist.md +0 -0
  61. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/rfc-template.md +0 -0
  62. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-architecture-design.md +0 -0
  63. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-conceptual-design.md +0 -0
  64. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-impl-interface-design.md +0 -0
  65. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-drafts-readme.md +0 -0
  66. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-impl-readme.md +0 -0
  67. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-platonic.yml +0 -0
  68. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-history.md +0 -0
  69. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-index.md +0 -0
  70. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-namings.md +0 -0
  71. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-standard.md +0 -0
  72. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/BRAINSTORM/brainstorm.md +0 -0
  73. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-code.md +0 -0
  74. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-create-guide.md +0 -0
  75. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-full.md +0 -0
  76. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-update-guide.md +0 -0
  77. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-validate-guide.md +0 -0
  78. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-plan-modular-specs.md +0 -0
  79. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-architecture.md +0 -0
  80. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-conceptual.md +0 -0
  81. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-impl-interface.md +0 -0
  82. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scaffold.md +0 -0
  83. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scan.md +0 -0
  84. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/REFERENCE.md +0 -0
  85. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/REVIEW/review-spec-compliance.md +0 -0
  86. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-compliance.md +0 -0
  87. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-taxonomy.md +0 -0
  88. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-history.md +0 -0
  89. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-index.md +0 -0
  90. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-namings.md +0 -0
  91. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-refine.md +0 -0
  92. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-validate-consistency.md +0 -0
  93. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-overview.md +0 -0
  94. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-1.md +0 -0
  95. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-2.md +0 -0
  96. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-3.md +0 -0
  97. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/SKILL.md +0 -0
  98. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-deep.json +0 -0
  99. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-first.json +0 -0
  100. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/idea-first.json +0 -0
  101. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/loop.json +0 -0
  102. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/paper-pipeline.json +0 -0
  103. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/rapid.json +0 -0
  104. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/rigor-first.json +0 -0
  105. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/stance-first.json +0 -0
  106. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/systematic-review.json +0 -0
  107. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/verify-loop.json +0 -0
  108. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/references/backends.md +0 -0
  109. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-ars-omr.md +0 -0
  110. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-omr-ars.md +0 -0
  111. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/references/routing.md +0 -0
  112. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/scripts/csljson_to_omr.py +0 -0
  113. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/builtin_skills/research-bootstrap/scripts/papers_index_to_csljson.py +0 -0
  114. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/__init__.py +0 -0
  115. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/__main__.py +0 -0
  116. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/completion/__init__.py +0 -0
  117. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/completion/context.py +0 -0
  118. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/completion/engine.py +0 -0
  119. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/doctor_cmd.py +0 -0
  120. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/doctor_fix_cmd.py +0 -0
  121. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/progress.py +0 -0
  122. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/serve_cmd.py +0 -0
  123. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/setup_cmd.py +0 -0
  124. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/shell/__init__.py +0 -0
  125. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/shell/_fj.zsh +0 -0
  126. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/shell/fj.bash +0 -0
  127. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/stream.py +0 -0
  128. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/threads.py +0 -0
  129. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/cli/tool_stream.py +0 -0
  130. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/__init__.py +0 -0
  131. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backend.py +0 -0
  132. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/__init__.py +0 -0
  133. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/direct.py +0 -0
  134. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/fake.py +0 -0
  135. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/__init__.py +0 -0
  136. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/adapter.py +0 -0
  137. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/admission.py +0 -0
  138. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/backend.py +0 -0
  139. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/errors.py +0 -0
  140. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/mode_gate.py +0 -0
  141. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/pool.py +0 -0
  142. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/request.py +0 -0
  143. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/backends/isolation/workspace.py +0 -0
  144. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/bootstrap.py +0 -0
  145. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/bridges/nano/__init__.py +0 -0
  146. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/bridges/nano/adapter.py +0 -0
  147. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/bridges/nano/backend.py +0 -0
  148. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/bridges/nano/mapping.py +0 -0
  149. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/events.py +0 -0
  150. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/identity.py +0 -0
  151. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/modes.py +0 -0
  152. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/protocol.py +0 -0
  153. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/tool_results.py +0 -0
  154. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/core/tool_stream.py +0 -0
  155. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/__init__.py +0 -0
  156. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/acp/__init__.py +0 -0
  157. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/acp/agent.py +0 -0
  158. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/acp/routes.py +0 -0
  159. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/config.py +0 -0
  160. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/http/__init__.py +0 -0
  161. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/http/app.py +0 -0
  162. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/http/auth.py +0 -0
  163. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/__init__.py +0 -0
  164. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/errors.py +0 -0
  165. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/projection.py +0 -0
  166. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/routes.py +0 -0
  167. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/schemas.py +0 -0
  168. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/service.py +0 -0
  169. {flowjet-2.0.3 → flowjet-2.0.4}/src/flowjet/server/openai_compat/store.py +0 -0
@@ -10,6 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [2.0.4] — 2026-09-19
14
+
15
+ ### Changed
16
+
17
+ - Bumped `soothe` lower bound to `1.0.13` (was `1.0.12`). soothe-nano stays
18
+ pinned at `>=1.2.28,<1.3.0`.
19
+
13
20
  ## [2.0.3] — 2026-09-18
14
21
 
15
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowjet
3
- Version: 2.0.3
3
+ Version: 2.0.4
4
4
  Summary: FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by soothe-nano
5
5
  Project-URL: Homepage, https://github.com/caesar0301/flowjet
6
6
  Project-URL: Repository, https://github.com/caesar0301/flowjet
@@ -23,7 +23,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Python: <3.15,>=3.11
25
25
  Requires-Dist: soothe-nano<1.3.0,>=1.2.28
26
- Requires-Dist: soothe<2.0.0,>=1.0.12
26
+ Requires-Dist: soothe<2.0.0,>=1.0.13
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: httpx>=0.27.0; extra == 'dev'
29
29
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flowjet"
7
- version = "2.0.3"
7
+ version = "2.0.4"
8
8
  description = "FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by soothe-nano"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -31,7 +31,7 @@ classifiers = [
31
31
  dependencies = [
32
32
  # Default CLI runtime (FLOWJET_BACKEND=soothe). soothe is a host layer over
33
33
  # soothe-nano, so nano stays pinned explicitly: soothe only caps it at <2.
34
- "soothe>=1.0.12,<2.0.0",
34
+ "soothe>=1.0.13,<2.0.0",
35
35
  "soothe-nano>=1.2.28,<1.3.0",
36
36
  ]
37
37
 
@@ -8,14 +8,12 @@ Nano bootstrap, config loading and the builtin-skill root now live in
8
8
  from __future__ import annotations
9
9
 
10
10
  import logging
11
- import os
12
- import sys
13
- from logging.handlers import RotatingFileHandler
14
11
  from pathlib import Path
15
12
  from typing import Any
16
13
 
17
14
  from soothe_nano.config import SootheConfig
18
15
 
16
+ from flowjet.cli.cli import _CompactConsoleFormatter
19
17
  from flowjet.core.bootstrap import (
20
18
  BUILTIN_SKILLS_DIR,
21
19
  FJ_CORE_SKILL_NAMES,
@@ -31,6 +29,27 @@ from flowjet.core.modes import CLI_PROFILE, apply_profile
31
29
 
32
30
  logger = logging.getLogger(__name__)
33
31
 
32
+ # ---------------------------------------------------------------------------
33
+ # CLI logging (delegated to ``flowjet.cli.cli`` at call time)
34
+ # ---------------------------------------------------------------------------
35
+
36
+
37
+ def configure_cli_logging(*, verbose: bool = False) -> None:
38
+ """Quiet the console for one-shot CLI use (see ``flowjet.cli.cli``).
39
+
40
+ Delegated dynamically so monkeypatching ``flowjet.cli.cli.configure_cli_logging``
41
+ (as tests do via ``monkeypatch.setattr(cli, ...)``) is honored here too.
42
+ """
43
+ from flowjet.cli import cli as _cli
44
+
45
+ _cli.configure_cli_logging(verbose=verbose)
46
+
47
+
48
+ def silence_after_plugins(*, verbose: bool = False) -> None:
49
+ """Re-apply quieting after plugin imports (belt-and-suspenders)."""
50
+ configure_cli_logging(verbose=verbose)
51
+
52
+
34
53
  # ---------------------------------------------------------------------------
35
54
  # Builtin skills (re-exported; owned by flowjet.core.bootstrap)
36
55
  # ---------------------------------------------------------------------------
@@ -57,73 +76,6 @@ def apply_fj_defaults(config: SootheConfig) -> SootheConfig:
57
76
  return apply_profile(config, CLI_PROFILE)
58
77
 
59
78
 
60
- # ---------------------------------------------------------------------------
61
- # CLI logging
62
- # ---------------------------------------------------------------------------
63
-
64
- _BROWSER_USE_SETUP_LOGGING = "BROWSER_USE_SETUP_LOGGING"
65
- _FJ_CONSOLE_HANDLER = "fj-console"
66
-
67
-
68
- class _CompactConsoleFormatter(logging.Formatter):
69
- """One-line console records without exception tracebacks."""
70
-
71
- def formatException(self, ei: object) -> str: # noqa: N802 - logging API
72
- return ""
73
-
74
- def format(self, record: logging.LogRecord) -> str:
75
- # logger.exception sets exc_info; drop it so format() stays one line.
76
- record.exc_info = None
77
- record.exc_text = None
78
- return super().format(record)
79
-
80
-
81
- def configure_cli_logging(*, verbose: bool = False) -> None:
82
- """Quiet the console for one-shot CLI use.
83
-
84
- - Opt out of ``browser_use`` import-time root logger setup when unset.
85
- - Remove existing root stream handlers (stderr/stdout) so init INFO
86
- lines do not interleave with the agent answer.
87
- - Prevent ``lastResort`` traceback dumps from soothe tool failures.
88
- - When ``verbose``, show WARNING+ as single-line messages on stderr.
89
- """
90
- os.environ.setdefault(_BROWSER_USE_SETUP_LOGGING, "false")
91
- _remove_root_console_handlers()
92
- root = logging.getLogger()
93
- if verbose:
94
- handler = logging.StreamHandler(sys.stderr)
95
- handler.set_name(_FJ_CONSOLE_HANDLER)
96
- handler.setLevel(logging.WARNING)
97
- handler.setFormatter(_CompactConsoleFormatter("%(message)s"))
98
- root.addHandler(handler)
99
- elif not root.handlers:
100
- null = logging.NullHandler()
101
- null.set_name(_FJ_CONSOLE_HANDLER)
102
- root.addHandler(null)
103
- if root.level < logging.WARNING:
104
- root.setLevel(logging.WARNING)
105
-
106
-
107
- def silence_after_plugins(*, verbose: bool = False) -> None:
108
- """Re-apply quieting after plugin imports (belt-and-suspenders)."""
109
- configure_cli_logging(verbose=verbose)
110
-
111
-
112
- def _remove_root_console_handlers() -> None:
113
- root = logging.getLogger()
114
- for handler in list(root.handlers):
115
- if isinstance(handler, RotatingFileHandler):
116
- continue
117
- if isinstance(handler, logging.FileHandler):
118
- continue
119
- if isinstance(handler, logging.StreamHandler) or isinstance(handler, logging.NullHandler):
120
- root.removeHandler(handler)
121
- try:
122
- handler.close()
123
- except Exception: # pragma: no cover - defensive
124
- pass
125
-
126
-
127
79
  # ---------------------------------------------------------------------------
128
80
  # Agent build
129
81
  # ---------------------------------------------------------------------------
@@ -165,6 +117,7 @@ async def build_agent(
165
117
  __all__ = [
166
118
  "BUILTIN_SKILLS_DIR",
167
119
  "FJ_CORE_SKILL_NAMES",
120
+ "_CompactConsoleFormatter",
168
121
  "apply_fj_defaults",
169
122
  "build_agent",
170
123
  "configure_cli_logging",
@@ -8,18 +8,113 @@ from __future__ import annotations
8
8
  import argparse
9
9
  import asyncio
10
10
  import contextlib
11
+ import logging
12
+ import os
11
13
  import sys
12
14
  from collections.abc import Coroutine
15
+ from logging.handlers import RotatingFileHandler
13
16
  from pathlib import Path
14
17
  from typing import Any
15
18
 
16
19
  from flowjet.cli import __version__
17
- from flowjet.cli.agent import configure_cli_logging
18
20
 
19
21
  FORMAL_CLI = "flowjet"
20
22
  CLI_ALIASES = frozenset({"fj", "fjf", FORMAL_CLI})
21
23
 
22
24
 
25
+ # ---------------------------------------------------------------------------
26
+ # CLI logging — stdlib only, kept here so ``fj -V`` / ``fj -h`` never pay the
27
+ # heavy ``soothe_nano`` / ``flowjet.core.bootstrap`` import cost.
28
+ # ---------------------------------------------------------------------------
29
+
30
+ _BROWSER_USE_SETUP_LOGGING = "BROWSER_USE_SETUP_LOGGING"
31
+ _FJ_CONSOLE_HANDLER = "fj-console"
32
+
33
+
34
+ class _CompactConsoleFormatter(logging.Formatter):
35
+ """One-line console records without exception tracebacks."""
36
+
37
+ def formatException(self, ei: object) -> str: # noqa: N802 - logging API
38
+ return ""
39
+
40
+ def format(self, record: logging.LogRecord) -> str:
41
+ # logger.exception sets exc_info; drop it so format() stays one line.
42
+ record.exc_info = None
43
+ record.exc_text = None
44
+ return super().format(record)
45
+
46
+
47
+ _FILE_LOGGING_INSTALLED = False
48
+
49
+
50
+ def configure_cli_logging(*, verbose: bool = False) -> None:
51
+ """Quiet the console for one-shot CLI use and enable file logging by default.
52
+
53
+ - Opt out of ``browser_use`` import-time root logger setup when unset.
54
+ - Remove existing root stream handlers (stderr/stdout) so init INFO
55
+ lines do not interleave with the agent answer.
56
+ - Prevent ``lastResort`` traceback dumps from soothe tool failures.
57
+ - When ``verbose``, show WARNING+ as single-line messages on stderr.
58
+ - Attach a rotating file handler at ``<FLOWJET_HOME>/logs/cli.log`` so
59
+ every CLI run is recorded by default (idempotent across re-invocations
60
+ within the same process, e.g. tests).
61
+ """
62
+ os.environ.setdefault(_BROWSER_USE_SETUP_LOGGING, "false")
63
+ _remove_root_console_handlers()
64
+ root = logging.getLogger()
65
+ if verbose:
66
+ handler = logging.StreamHandler(sys.stderr)
67
+ handler.set_name(_FJ_CONSOLE_HANDLER)
68
+ handler.setLevel(logging.WARNING)
69
+ handler.setFormatter(_CompactConsoleFormatter("%(message)s"))
70
+ root.addHandler(handler)
71
+ elif not root.handlers:
72
+ null = logging.NullHandler()
73
+ null.set_name(_FJ_CONSOLE_HANDLER)
74
+ root.addHandler(null)
75
+ if root.level < logging.WARNING:
76
+ root.setLevel(logging.WARNING)
77
+
78
+ _ensure_cli_file_logging()
79
+
80
+
81
+ def _ensure_cli_file_logging() -> None:
82
+ """Attach the rotating file handler for CLI runs (idempotent)."""
83
+ global _FILE_LOGGING_INSTALLED
84
+ if _FILE_LOGGING_INSTALLED:
85
+ return
86
+ try:
87
+ from flowjet.core.home import cli_log_path, ensure_flowjet_home
88
+
89
+ ensure_flowjet_home()
90
+ from soothe_nano.logging.setup import setup_logging
91
+
92
+ setup_logging(log_file=str(cli_log_path()), extra_logger_names=("flowjet",))
93
+ _FILE_LOGGING_INSTALLED = True
94
+ except Exception: # pragma: no cover - logging must never break the CLI
95
+ pass
96
+
97
+
98
+ def silence_after_plugins(*, verbose: bool = False) -> None:
99
+ """Re-apply quieting after plugin imports (belt-and-suspenders)."""
100
+ configure_cli_logging(verbose=verbose)
101
+
102
+
103
+ def _remove_root_console_handlers() -> None:
104
+ root = logging.getLogger()
105
+ for handler in list(root.handlers):
106
+ if isinstance(handler, RotatingFileHandler):
107
+ continue
108
+ if isinstance(handler, logging.FileHandler):
109
+ continue
110
+ if isinstance(handler, logging.StreamHandler) or isinstance(handler, logging.NullHandler):
111
+ root.removeHandler(handler)
112
+ try:
113
+ handler.close()
114
+ except Exception: # pragma: no cover - defensive
115
+ pass
116
+
117
+
23
118
  def resolve_cli_prog(argv0: str | None = None) -> str:
24
119
  """Return the invoked command name for help / version strings.
25
120
 
@@ -217,6 +312,13 @@ class _HelpFormatter(argparse.RawDescriptionHelpFormatter):
217
312
  def __init__(self, prog: str) -> None:
218
313
  super().__init__(prog, max_help_position=26, width=92)
219
314
 
315
+ def _format_action(self, action: argparse.Action) -> str: # type: ignore[override]
316
+ # Resolve lazy help strings only when help is actually rendered, so
317
+ # ``-V`` never pays for the import behind ``_default_config_help``.
318
+ if isinstance(action.help, _LazyHelp):
319
+ action.help = str(action.help)
320
+ return super()._format_action(action)
321
+
220
322
 
221
323
  def _default_config_help() -> str:
222
324
  from flowjet.cli.agent import default_config_path
@@ -224,6 +326,26 @@ def _default_config_help() -> str:
224
326
  return f"Alternate nano.yml (default: {default_config_path()})"
225
327
 
226
328
 
329
+ class _LazyHelp:
330
+ """Resolve a help-string callable only when argparse reads it.
331
+
332
+ ``-V`` / ``-h``-without-our-flag paths never touch the help text, so the
333
+ heavy import behind ``_default_config_help`` stays deferred until a flag
334
+ that actually shows help (``-h``) or errors is invoked.
335
+ """
336
+
337
+ __slots__ = ("_factory", "_cached")
338
+
339
+ def __init__(self, factory: Any) -> None:
340
+ self._factory = factory
341
+ self._cached: str | None = None
342
+
343
+ def __str__(self) -> str:
344
+ if self._cached is None:
345
+ self._cached = self._factory()
346
+ return self._cached
347
+
348
+
227
349
  def _build_parser(prog: str | None = None) -> argparse.ArgumentParser:
228
350
  cli_prog = prog or resolve_cli_prog()
229
351
  parser = argparse.ArgumentParser(
@@ -297,7 +419,7 @@ def _build_parser(prog: str | None = None) -> argparse.ArgumentParser:
297
419
  "-c",
298
420
  "--config",
299
421
  metavar="PATH",
300
- help=_default_config_help(),
422
+ help=_LazyHelp(_default_config_help),
301
423
  )
302
424
  paths.add_argument(
303
425
  "-w",
@@ -324,7 +446,7 @@ def _build_setup_parser(prog: str | None = None) -> argparse.ArgumentParser:
324
446
  "-c",
325
447
  "--config",
326
448
  metavar="PATH",
327
- help=_default_config_help(),
449
+ help=_LazyHelp(_default_config_help),
328
450
  )
329
451
  return parser
330
452
 
@@ -565,8 +687,17 @@ def main_follow(argv: list[str] | None = None) -> int:
565
687
 
566
688
  def main(argv: list[str] | None = None) -> int:
567
689
  try:
568
- configure_cli_logging()
690
+ # Align SOOTHE_HOME with FLOWJET_HOME before any soothe import so
691
+ # config, data, and logs all land under ~/.flowjet. Must run first:
692
+ # soothe_sdk.paths binds SOOTHE_HOME at import time.
693
+ from flowjet.core.home import ensure_flowjet_home
694
+
695
+ ensure_flowjet_home()
696
+ # Parse first so ``-V`` / ``-h`` / ``--help`` exit before we touch the
697
+ # logging stack (and the heavy ``flowjet.cli.agent`` import it used to
698
+ # drag in). Version/help must stay instant.
569
699
  args = parse_args(argv)
700
+ configure_cli_logging()
570
701
  # Re-apply after parse so ``-v`` can enable compact console warnings.
571
702
  if getattr(args, "verbose", False):
572
703
  configure_cli_logging(verbose=True)
@@ -0,0 +1,127 @@
1
+ """Align ``SOOTHE_HOME`` with ``FLOWJET_HOME`` and enable file logging by default.
2
+
3
+ FlowJet ships its own home directory (``~/.flowjet``) distinct from Soothe's
4
+ default (``~/.soothe``). Soothe-nano reads ``SOOTHE_HOME`` once at import time
5
+ (``soothe_sdk.paths``), so this module must run *before* any soothe import.
6
+
7
+ Call :func:`ensure_flowjet_home` at the top of every entry point
8
+ (``flowjet.cli.cli.main``, ``flowjet.server.__main__.main``). It is idempotent
9
+ and safe to call from tests.
10
+
11
+ Resolution order for the shared home:
12
+ 1. ``FLOWJET_HOME`` env var (if set)
13
+ 2. ``SOOTHE_HOME`` env var (if set) — honours an operator who pinned Soothe's home
14
+ 3. ``~/.flowjet`` (default)
15
+
16
+ When ``SOOTHE_HOME`` is unset or still points at the Soothe default
17
+ (``~/.soothe``), it is repointed at the resolved FlowJet home so config, data,
18
+ and logs all land under one root.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ import os
25
+ from pathlib import Path
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _DEFAULT_SOOTHE_HOME = str(Path.home() / ".soothe")
30
+ _DEFAULT_FLOWJET_HOME = str(Path.home() / ".flowjet")
31
+
32
+ _ALIGNED = False
33
+
34
+
35
+ def resolve_home() -> Path:
36
+ """Return the shared home directory for FlowJet and Soothe.
37
+
38
+ Priority: ``FLOWJET_HOME`` > ``SOOTHE_HOME`` > ``~/.flowjet``.
39
+ """
40
+ env_fj = os.environ.get("FLOWJET_HOME")
41
+ if env_fj:
42
+ return Path(env_fj).expanduser()
43
+ env_soothe = os.environ.get("SOOTHE_HOME")
44
+ if env_soothe and env_soothe != _DEFAULT_SOOTHE_HOME:
45
+ return Path(env_soothe).expanduser()
46
+ return Path(_DEFAULT_FLOWJET_HOME).expanduser()
47
+
48
+
49
+ def ensure_flowjet_home() -> Path:
50
+ """Align ``SOOTHE_HOME`` with the FlowJet home and create the logs dir.
51
+
52
+ Idempotent. Returns the resolved home path. Safe to call before any soothe
53
+ module is imported — and it *must* be, because ``soothe_sdk.paths`` binds
54
+ ``SOOTHE_HOME`` at import time.
55
+ """
56
+ global _ALIGNED
57
+ home = resolve_home()
58
+
59
+ # Repoint SOOTHE_HOME at the FlowJet home unless an operator has already
60
+ # pinned it somewhere other than the Soothe default. ``setdefault`` keeps
61
+ # an explicit SOOTHE_HOME that is not the default.
62
+ current_soothe = os.environ.get("SOOTHE_HOME")
63
+ if current_soothe is None or current_soothe == _DEFAULT_SOOTHE_HOME:
64
+ os.environ["SOOTHE_HOME"] = str(home)
65
+
66
+ # SOOTHE_DATA_DIR follows SOOTHE_HOME/data by default in soothe_sdk.paths;
67
+ # only set it when unset so an explicit operator override wins.
68
+ os.environ.setdefault("SOOTHE_DATA_DIR", str(home / "data"))
69
+
70
+ # Ensure the logs directory exists so file handlers never fail on first write.
71
+ (home / "logs").mkdir(parents=True, exist_ok=True)
72
+
73
+ _ALIGNED = True
74
+ return home
75
+
76
+
77
+ def flowjet_home() -> Path:
78
+ """Return the aligned FlowJet home (aligns on first call if not yet done)."""
79
+ if not _ALIGNED:
80
+ return ensure_flowjet_home()
81
+ return resolve_home()
82
+
83
+
84
+ def log_dir() -> Path:
85
+ """Return the log directory under the FlowJet home."""
86
+ return flowjet_home() / "logs"
87
+
88
+
89
+ def cli_log_path() -> Path:
90
+ """Return the CLI log file path (``<home>/logs/cli.log``)."""
91
+ return log_dir() / "cli.log"
92
+
93
+
94
+ def setup_file_logging(
95
+ *,
96
+ log_file: str | Path | None = None,
97
+ extra_logger_names: tuple[str, ...] = (),
98
+ ) -> None:
99
+ """Configure rotating file logging under the FlowJet home.
100
+
101
+ Delegates to ``soothe_nano.logging.setup.setup_logging`` so the FlowJet
102
+ logger trees share the same rotating handler, formatter, and thread-id
103
+ context as Soothe. The default log file is ``<home>/logs/flowjet.log``;
104
+ pass ``log_file`` to override (e.g. ``cli.log`` for the CLI surface).
105
+
106
+ Args:
107
+ log_file: Optional log file path override.
108
+ extra_logger_names: Additional logger tree roots (e.g. ``("flowjet",)``)
109
+ to attach the same file handler to.
110
+ """
111
+ ensure_flowjet_home()
112
+ from soothe_nano.logging.setup import setup_logging
113
+
114
+ target = str(log_file) if log_file is not None else str(log_dir() / "flowjet.log")
115
+ # FlowJet's own logger trees share the handler with soothe_nano's.
116
+ names: tuple[str, ...] = ("flowjet", *extra_logger_names)
117
+ setup_logging(log_file=target, extra_logger_names=names)
118
+
119
+
120
+ __all__ = [
121
+ "cli_log_path",
122
+ "ensure_flowjet_home",
123
+ "flowjet_home",
124
+ "log_dir",
125
+ "resolve_home",
126
+ "setup_file_logging",
127
+ ]
@@ -35,6 +35,15 @@ def _create_dual_stack_socket(host: str, port: int) -> socket.socket:
35
35
 
36
36
 
37
37
  def main() -> None:
38
+ # Align SOOTHE_HOME with FLOWJET_HOME before any soothe import so config,
39
+ # data, and logs all land under ~/.flowjet. Must run before Settings() or
40
+ # any soothe_nano import: soothe_sdk.paths binds SOOTHE_HOME at import time.
41
+ from flowjet.core.home import ensure_flowjet_home, setup_file_logging
42
+
43
+ ensure_flowjet_home()
44
+ # Record server runs to <FLOWJET_HOME>/logs/flowjet.log by default.
45
+ setup_file_logging(extra_logger_names=("flowjet",))
46
+
38
47
  settings = Settings()
39
48
  # When the host is a wildcard (``::`` or ``0.0.0.0``), use a dual-stack
40
49
  # socket so both IPv4 and IPv6 clients can connect. This is essential
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes