fabric-vibecoding-settings 0.2__tar.gz → 0.3__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.
Files changed (176) hide show
  1. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/.gitignore +5 -1
  2. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/PKG-INFO +1 -4
  3. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/CLAUDE.md +2 -2
  4. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/AGENTS.md +2 -2
  5. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/config.toml +14 -1
  6. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/setup/setup.ps1 +29 -13
  7. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/setup/setup.sh +31 -14
  8. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/runtime_cli.py +1 -1
  9. fabric_vibecoding_settings-0.3/cli/tools/auth/refresh.py +333 -0
  10. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/pyproject.toml +1 -6
  11. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/Dockerfile +10 -4
  12. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/__init__.py +1 -1
  13. fabric_vibecoding_settings-0.3/server/__main__.py +50 -0
  14. fabric_vibecoding_settings-0.3/server/api/__init__.py +0 -0
  15. fabric_vibecoding_settings-0.3/server/api/routes.py +810 -0
  16. fabric_vibecoding_settings-0.3/server/app.py +277 -0
  17. fabric_vibecoding_settings-0.3/server/auth/__init__.py +32 -0
  18. fabric_vibecoding_settings-0.3/server/auth/middleware.py +288 -0
  19. fabric_vibecoding_settings-0.3/server/auth/repository.py +233 -0
  20. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/builders/build-graph.py +2 -2
  21. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/builders/graph_build/visualize.py +2 -2
  22. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/builder.py +7 -0
  23. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/schema.py +5 -0
  24. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/search.py +17 -8
  25. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/writes.py +23 -6
  26. fabric_vibecoding_settings-0.3/server/managed/.gitignore +2 -0
  27. fabric_vibecoding_settings-0.3/server/managed/content/memory/.gitkeep +0 -0
  28. fabric_vibecoding_settings-0.3/server/managed/content/rules/.gitkeep +0 -0
  29. fabric_vibecoding_settings-0.3/server/managed/content/skill-fixes/.gitkeep +0 -0
  30. fabric_vibecoding_settings-0.3/server/managed/skills/.gitkeep +0 -0
  31. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/graph/tools.py +1 -1
  32. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/_validation/install_package.py +2 -1
  33. fabric_vibecoding_settings-0.3/tests/test_admin_api.py +411 -0
  34. fabric_vibecoding_settings-0.3/tests/test_api_key_repository.py +112 -0
  35. fabric_vibecoding_settings-0.3/tests/test_auth_refresh.py +246 -0
  36. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_graph_search.py +2 -2
  37. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_graph_visualize.py +8 -7
  38. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_layout.py +6 -1
  39. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_server_auth.py +82 -11
  40. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_skill_split_coverage.py +1 -1
  41. fabric_vibecoding_settings-0.2/cli/tools/auth/refresh.py +0 -237
  42. fabric_vibecoding_settings-0.2/server/__main__.py +0 -29
  43. fabric_vibecoding_settings-0.2/server/app.py +0 -67
  44. fabric_vibecoding_settings-0.2/server/auth/__init__.py +0 -48
  45. fabric_vibecoding_settings-0.2/server/auth/middleware.py +0 -151
  46. fabric_vibecoding_settings-0.2/server/auth/repository.py +0 -267
  47. fabric_vibecoding_settings-0.2/server/docker-compose.yml +0 -53
  48. fabric_vibecoding_settings-0.2/tests/test_api_key_repository.py +0 -233
  49. fabric_vibecoding_settings-0.2/tests/test_auth_refresh.py +0 -136
  50. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/LICENSE +0 -0
  51. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/README.md +0 -0
  52. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/agents/developer.md +0 -0
  53. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/agents/operator.md +0 -0
  54. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/agents/orchestrator.md +0 -0
  55. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/agents/tester.md +0 -0
  56. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/claude/settings.local.json +0 -0
  57. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/agents/developer.toml +0 -0
  58. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/agents/operator.toml +0 -0
  59. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/agents/orchestrator.toml +0 -0
  60. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/codex/agents/tester.toml +0 -0
  61. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/shared/.env.example +0 -0
  62. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/shared/.gitignore.fragment +0 -0
  63. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/shared/scaffold/data/sandbox/.gitkeep +0 -0
  64. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/profiles/shared/scaffold/workspace/.gitkeep +0 -0
  65. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/__init__.py +0 -0
  66. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/__main__.py +0 -0
  67. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/cli.py +0 -0
  68. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/commands/__init__.py +0 -0
  69. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/commands/_common.py +0 -0
  70. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/commands/check.py +0 -0
  71. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/commands/install.py +0 -0
  72. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/commands/refresh.py +0 -0
  73. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/__init__.py +0 -0
  74. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/bootstrap.py +0 -0
  75. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/files.py +0 -0
  76. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/gitignore.py +0 -0
  77. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/markers.py +0 -0
  78. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/paths.py +0 -0
  79. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/profiles.py +0 -0
  80. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/core/version_check.py +0 -0
  81. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/src/fabric_skills_settings/logging_config.py +0 -0
  82. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lakehouse/list-tables.py +0 -0
  83. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lint/__init__.py +0 -0
  84. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lint/__main__.py +0 -0
  85. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lint/core.py +0 -0
  86. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lint/de_faker_seed.py +0 -0
  87. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/lint/sec_no_hardcoded_secrets.py +0 -0
  88. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/notebook/build.py +0 -0
  89. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/notebook/deploy.py +0 -0
  90. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/notebook/smoke-test.ps1 +0 -0
  91. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/notebook/smoke-test.sh +0 -0
  92. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/pipeline/manage.py +0 -0
  93. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/precommit/__init__.py +0 -0
  94. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/precommit/pre-commit-check.ps1 +0 -0
  95. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/precommit/pre-commit-check.sh +0 -0
  96. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/workspace/init.py +0 -0
  97. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/workspace/pick.py +0 -0
  98. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/workspace/switch.py +0 -0
  99. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/cli/tools/workspace/transfer.py +0 -0
  100. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/.dockerignore +0 -0
  101. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/audit.py +0 -0
  102. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/auth/tokens.py +0 -0
  103. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/builders/build-agent-capability-graph.py +0 -0
  104. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/builders/graph_build/__init__.py +0 -0
  105. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/builders/graph_build/agent_capabilities.py +0 -0
  106. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/diagnostics/smoke-test-diagnostics.md +0 -0
  107. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/entry.md +0 -0
  108. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/indexes/skills-index.md +0 -0
  109. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/integrations/rtk.md +0 -0
  110. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/layout/directory-layout.md +0 -0
  111. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/layout/tool-layout.md +0 -0
  112. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/rules/data-engineering.md +0 -0
  113. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/rules/fabric-platform.md +0 -0
  114. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/rules/notebook-authoring.md +0 -0
  115. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/rules/security.md +0 -0
  116. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/semantic/semantic-models.md +0 -0
  117. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/session/operating-rules.md +0 -0
  118. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/session/session-start.md +0 -0
  119. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/workflow/notebook-workflow.md +0 -0
  120. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/workflow/pipeline-structure.md +0 -0
  121. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/content/workflow/workspace-management.md +0 -0
  122. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/__init__.py +0 -0
  123. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/extract.py +0 -0
  124. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/lock.py +0 -0
  125. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/graph/store.py +0 -0
  126. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/caveman/SKILL.md +0 -0
  127. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-ingest/SKILL.md +0 -0
  128. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-model/SKILL.md +0 -0
  129. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/SKILL.md +0 -0
  130. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/diagnosing-opaque-failures.md +0 -0
  131. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/full-example.md +0 -0
  132. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/mlflow-platform-limits.md +0 -0
  133. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/must.md +0 -0
  134. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/prefer-avoid.md +0 -0
  135. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-notebook-loop/sections/the-loop.md +0 -0
  136. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-ops/SKILL.md +0 -0
  137. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-pipeline/SKILL.md +0 -0
  138. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-transform/SKILL.md +0 -0
  139. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/fabric-validate/SKILL.md +0 -0
  140. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/git-commit/SKILL.md +0 -0
  141. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/grill-me/SKILL.md +0 -0
  142. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/mock-data/SKILL.md +0 -0
  143. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/prd/SKILL.md +0 -0
  144. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/rtk/SKILL.md +0 -0
  145. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/skills/semantic-model/SKILL.md +0 -0
  146. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/__init__.py +0 -0
  147. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/data/__init__.py +0 -0
  148. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/data/mock-data-generator.py +0 -0
  149. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/data/tools.py +0 -0
  150. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/graph/__init__.py +0 -0
  151. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/semantic_model/__init__.py +0 -0
  152. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/semantic_model/inspect.py +0 -0
  153. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/semantic_model/tools.py +0 -0
  154. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/utils/script_runner.py +0 -0
  155. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/validate/__init__.py +0 -0
  156. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/validate/pipeline-lineage.py +0 -0
  157. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/server/tools/validate/tools.py +0 -0
  158. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/_validation/__init__.py +0 -0
  159. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/_validation/agent_guidance.py +0 -0
  160. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/conftest.py +0 -0
  161. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_agent_capability_graph.py +0 -0
  162. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_agent_guidance.py +0 -0
  163. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_build.py +0 -0
  164. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_extract.py +0 -0
  165. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_graph_builder.py +0 -0
  166. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_graph_store.py +0 -0
  167. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_install_package.py +0 -0
  168. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_lint.py +0 -0
  169. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_local_helper_wrappers.py +0 -0
  170. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_manage.py +0 -0
  171. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_mcp_wrappers.py +0 -0
  172. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_mock_data_generator.py +0 -0
  173. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_profile_entrypoints.py +0 -0
  174. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_semantic_model_inspect.py +0 -0
  175. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_validator_profile_minimal.py +0 -0
  176. {fabric_vibecoding_settings-0.2 → fabric_vibecoding_settings-0.3}/tests/test_version_check.py +0 -0
@@ -13,6 +13,7 @@
13
13
  logs/
14
14
  src/
15
15
  !cli/src/
16
+ !frontend/src/
16
17
  fabric_notebooks/
17
18
  _delta_log/
18
19
  *.checkpoint.parquet
@@ -60,4 +61,7 @@ Thumbs.db
60
61
  .vscode/
61
62
  .idea/
62
63
  *.swp
63
- .pytest_cache/
64
+ .pytest_cache/
65
+ .agents
66
+ .claude
67
+ skills-lock.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabric-vibecoding-settings
3
- Version: 0.2
3
+ Version: 0.3
4
4
  Summary: Install Microsoft Fabric agent profiles (Claude Code and Codex) into a target repository.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -17,9 +17,6 @@ Requires-Dist: pydantic>=2.7; extra == 'server'
17
17
  Requires-Dist: pyjwt>=2.8; extra == 'server'
18
18
  Requires-Dist: rank-bm25>=0.2.2; extra == 'server'
19
19
  Requires-Dist: uvicorn>=0.30; extra == 'server'
20
- Provides-Extra: server-azure
21
- Requires-Dist: azure-identity>=1.16; extra == 'server-azure'
22
- Requires-Dist: azure-storage-blob>=12.19; extra == 'server-azure'
23
20
  Description-Content-Type: text/markdown
24
21
 
25
22
  # Fabric Agent Pack
@@ -12,8 +12,8 @@ directly; use the graph.
12
12
  ## How to work
13
13
 
14
14
  The `fabric-server` MCP is a separate process — a Docker container the
15
- human starts with `docker compose up` from the source repo's `server/`
16
- directory before opening Claude. The project MCP config generated by
15
+ human starts with `docker compose up` from the source repo root
16
+ before opening Claude. The project MCP config generated by
17
17
  `fabric-vibe setup` points clients to its Fabric and graph tools. If
18
18
  `tools/list` returns nothing the container probably isn't running.
19
19
 
@@ -12,8 +12,8 @@ directly; use the graph.
12
12
  ## How to work
13
13
 
14
14
  The `fabric-server` MCP is a separate process — a Docker container the
15
- human starts with `docker compose up` from the source repo's `server/`
16
- directory before opening Codex. The project MCP config generated by
15
+ human starts with `docker compose up` from the source repo root
16
+ before opening Codex. The project MCP config generated by
17
17
  `fabric-vibe setup` points clients to its Fabric and graph tools. If
18
18
  `tools/list` returns nothing the container probably isn't running.
19
19
 
@@ -15,7 +15,20 @@ ignore_default_excludes = false
15
15
  # Single HTTP MCP server, served by the local Docker container
16
16
  # (`docker compose up` from the source repo's server/ directory).
17
17
  # The target bootstrap patches this URL for the local Fabric MCP server.
18
- [mcp_servers.fabric-server]
18
+ [mcp_servers.fabric_server]
19
19
  url = "http://127.0.0.1:8000/mcp"
20
20
  startup_timeout_ms = 20000
21
21
  tool_timeout_ms = 120000
22
+
23
+ [mcp_servers.fabric_server.tools.graph_get_entry]
24
+ approval_mode = "approve"
25
+
26
+ [mcp_servers.fabric_server.tools.graph_get_linked]
27
+ approval_mode = "approve"
28
+
29
+ [mcp_servers.fabric_server.tools.graph_get_node]
30
+ approval_mode = "approve"
31
+
32
+ [mcp_servers.fabric_server.tools.graph_search]
33
+ approval_mode = "approve"
34
+
@@ -14,12 +14,11 @@
14
14
  # 5. Prompts for FABRIC_CLIENT_SECRET and persists to the user's OS env
15
15
  # (NOT .env - secrets stay in the OS env).
16
16
  # 6. Prompts for MCP_SERVER_URL.
17
- # 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric-server]
17
+ # 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric_server]
18
18
  # url (if installed).
19
- # 8. Prompts for FABRIC_MCP_API_KEY (from server admin) and persists it to the
20
- # OS user environment (registry). Runs fabric-vibe auth refresh, which calls
21
- # /auth/login with the API key, receives a short-lived JWT, and writes it
22
- # into the MCP client headers.
19
+ # 8. Prompts for FABRIC_MCP_AUTH_URL (auth service base URL; defaults to
20
+ # MCP_SERVER_URL/api/auth) and FABRIC_MCP_API_KEY (from server admin).
21
+ # Runs fabric-vibe auth refresh to obtain a short-lived JWT.
23
22
  # 9. Verifies SPN auth by calling `fab api workspaces`.
24
23
  # 10. Runs fabric-vibe workspace init to populate workspaces.json.
25
24
  # 11. Prompts to select the active workspace.
@@ -179,11 +178,28 @@ if ($env:MCP_SERVER_URL) {
179
178
  if (-not $mcpServerUrl) { $mcpServerUrl = "http://127.0.0.1:8000" }
180
179
  }
181
180
 
181
+ # -- MCP auth URL ------------------------------------------------------------
182
+ # Base URL for the auth service (without /login or /refresh). This varies by
183
+ # deployment — e.g. a reverse proxy may expose it at /server/auth rather than
184
+ # /api/auth. fabric-vibe auth refresh appends /login or /refresh as needed.
185
+ Write-Host ""
186
+ Write-Host "-- MCP auth URL"
187
+ $defaultAuthUrl = "$($mcpServerUrl.TrimEnd('/'))/api/auth"
188
+ if ($env:FABRIC_MCP_AUTH_URL) {
189
+ Write-Host " FABRIC_MCP_AUTH_URL already set - keeping $($env:FABRIC_MCP_AUTH_URL)"
190
+ } else {
191
+ $authUrl = Read-Host " FABRIC_MCP_AUTH_URL [$defaultAuthUrl]"
192
+ if (-not $authUrl) { $authUrl = $defaultAuthUrl }
193
+ [System.Environment]::SetEnvironmentVariable("FABRIC_MCP_AUTH_URL", $authUrl, "User")
194
+ $env:FABRIC_MCP_AUTH_URL = $authUrl
195
+ $Actions.Add("FABRIC_MCP_AUTH_URL persisted to OS user environment (registry)")
196
+ }
197
+
182
198
  # -- MCP API key -------------------------------------------------------------
183
199
  # The MCP server validates this key and issues a short-lived JWT. fabric-vibe
184
- # auth refresh reads FABRIC_MCP_API_KEY, calls /auth/login, and injects the JWT
185
- # into the MCP client headers. The key is persisted to the OS user environment
186
- # (registry), not to .env, so it is never committed to the repository.
200
+ # auth refresh reads FABRIC_MCP_API_KEY, calls FABRIC_MCP_AUTH_URL/login, and
201
+ # injects the JWT into the MCP client auth config. The key is persisted to the OS
202
+ # user environment (registry), not to .env, so it is never committed.
187
203
  Write-Host ""
188
204
  Write-Host "-- MCP API key"
189
205
  if ($env:FABRIC_MCP_API_KEY) {
@@ -198,22 +214,22 @@ if ($env:FABRIC_MCP_API_KEY) {
198
214
  }
199
215
 
200
216
  # -- MCP client config (.mcp.json) -------------------------------------------
201
- # Write url only; fabric-vibe auth refresh calls /auth/login with FABRIC_MCP_API_KEY
202
- # and writes the returned JWT into the MCP client headers below.
217
+ # Write url only; fabric-vibe auth refresh calls FABRIC_MCP_AUTH_URL/login with
218
+ # FABRIC_MCP_API_KEY and writes the returned JWT into the MCP client auth config below.
203
219
  $McpJson = Join-Path $ProjectRoot ".mcp.json"
204
220
  $mcpUrl = "$($mcpServerUrl.TrimEnd('/'))/mcp"
205
221
  $mcpDoc = [ordered]@{ mcpServers = [ordered]@{ "fabric-server" = [ordered]@{ type = "http"; url = $mcpUrl } } }
206
222
  $mcpDoc | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $McpJson -Encoding utf8
207
223
  $Actions.Add(".mcp.json written ($mcpUrl)")
208
224
 
209
- # Keep Codex's MCP config url aligned (auth header is written by fabric-vibe auth refresh).
225
+ # Keep Codex's MCP config url aligned (auth config is written by fabric-vibe auth refresh).
210
226
  $CodexConfig = Join-Path $ProjectRoot ".codex/config.toml"
211
227
  if (Test-Path -LiteralPath $CodexConfig) {
212
228
  $lines = [System.IO.File]::ReadAllLines($CodexConfig)
213
229
  $out = [System.Collections.Generic.List[string]]::new()
214
230
  $inFabric = $false
215
231
  foreach ($line in $lines) {
216
- if ($line -match '^\[mcp_servers\.fabric-server\]') { $inFabric = $true; $out.Add($line); continue }
232
+ if ($line -match '^\[mcp_servers\.(?:"fabric-server"|fabric-server|fabric_server)\]') { $inFabric = $true; $out.Add("[mcp_servers.fabric_server]"); continue }
217
233
  if ($line -match '^\[') { $inFabric = $false }
218
234
  if ($inFabric -and $line -match '^\s*url\s*=') { $out.Add("url = `"$mcpUrl`""); continue }
219
235
  $out.Add($line)
@@ -227,7 +243,7 @@ Write-Host ""
227
243
  Write-Host "-- MCP client token"
228
244
  fabric-vibe auth refresh
229
245
  if ($LASTEXITCODE -ne 0) { Write-Warning "MCP token refresh failed; run 'fabric-vibe auth refresh' manually." }
230
- else { $Actions.Add("MCP token written to MCP client headers") }
246
+ else { $Actions.Add("MCP token written to MCP client auth config") }
231
247
 
232
248
  # -- Authenticate ------------------------------------------------------------
233
249
  # Use explicit SPN login. fab does NOT pick up AZURE_* env vars implicitly —
@@ -4,8 +4,7 @@
4
4
  # Scope: configure the user's laptop so Claude/Codex can talk to the Fabric
5
5
  # MCP server AND drive the local Fabric CLI (fab) for notebook / pipeline /
6
6
  # lakehouse / workspace work. The MCP server itself lives in Docker — start
7
- # it separately with `docker compose up --build` from the source repo's
8
- # `server/` directory.
7
+ # it separately with `docker compose up --build` from the source repo root.
9
8
  #
10
9
  # This script:
11
10
  # 1. Verifies uv is installed.
@@ -16,10 +15,11 @@
16
15
  # profile (NOT .env — secrets stay in the OS env).
17
16
  # 6. Prompts for MCP_SERVER_URL and the FABRIC_MCP_API_KEY (from server admin).
18
17
  # The API key is persisted to the shell profile (chmod 600), not .env.
19
- # 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric-server]
18
+ # 7. Writes .mcp.json and patches .codex/config.toml's [mcp_servers.fabric_server]
20
19
  # url (if installed).
21
- # 8. Runs fabric-vibe auth refresh, which calls /auth/login with the API key,
22
- # receives a short-lived JWT, and writes it into the MCP client headers.
20
+ # 8. Prompts for FABRIC_MCP_AUTH_URL (auth service base URL; defaults to
21
+ # MCP_SERVER_URL/api/auth) and FABRIC_MCP_API_KEY (from server admin).
22
+ # Runs fabric-vibe auth refresh to obtain a short-lived JWT.
23
23
  # 9. Verifies SPN auth by calling `fab api workspaces`.
24
24
  # 10. Runs fabric-vibe workspace init to populate workspaces.json.
25
25
  # 11. Prompts to select the active workspace.
@@ -205,11 +205,28 @@ else
205
205
  mcp_server_url="${mcp_server_url:-http://127.0.0.1:8000}"
206
206
  fi
207
207
 
208
+ # ── MCP auth URL ─────────────────────────────────────────────────────────────
209
+ # Base URL for the auth service (without /login or /refresh). This varies by
210
+ # deployment — e.g. a reverse proxy may expose it at /server/auth rather than
211
+ # /api/auth. fabric-vibe auth refresh appends /login or /refresh as needed.
212
+ echo ""
213
+ echo "-- MCP auth URL"
214
+ _default_auth_url="${mcp_server_url%/}/api/auth"
215
+ if [[ -n "${FABRIC_MCP_AUTH_URL:-}" ]]; then
216
+ echo " FABRIC_MCP_AUTH_URL already set — keeping ${FABRIC_MCP_AUTH_URL}"
217
+ else
218
+ read -rp " FABRIC_MCP_AUTH_URL [${_default_auth_url}]: " FABRIC_MCP_AUTH_URL
219
+ FABRIC_MCP_AUTH_URL="${FABRIC_MCP_AUTH_URL:-${_default_auth_url}}"
220
+ export FABRIC_MCP_AUTH_URL
221
+ persist_secret "FABRIC_MCP_AUTH_URL" "${FABRIC_MCP_AUTH_URL}"
222
+ actions+=("FABRIC_MCP_AUTH_URL persisted to shell profile")
223
+ fi
224
+
208
225
  # ── MCP API key ───────────────────────────────────────────────────────────────
209
226
  # The MCP server validates this key and issues a short-lived JWT. fabric-vibe
210
- # auth refresh reads FABRIC_MCP_API_KEY, calls /auth/login, and injects the JWT
211
- # into the MCP client headers. The key is persisted to the shell profile (chmod
212
- # 600), not to .env, so it is never committed to the repository.
227
+ # auth refresh reads FABRIC_MCP_API_KEY, calls FABRIC_MCP_AUTH_URL/login, and
228
+ # injects the JWT into the MCP client auth config. The key is persisted to the
229
+ # shell profile (chmod 600), not to .env, so it is never committed.
213
230
  echo ""
214
231
  echo "-- MCP API key"
215
232
  if [[ -n "${FABRIC_MCP_API_KEY:-}" ]]; then
@@ -224,8 +241,8 @@ else
224
241
  fi
225
242
 
226
243
  # ── MCP client config (.mcp.json) ─────────────────────────────────────────────
227
- # Write url only; fabric-vibe auth refresh calls /auth/login with FABRIC_MCP_API_KEY
228
- # and writes the returned JWT into the MCP client headers below.
244
+ # Write url only; fabric-vibe auth refresh calls FABRIC_MCP_AUTH_URL/login with
245
+ # FABRIC_MCP_API_KEY and writes the returned JWT into the MCP client auth config below.
229
246
  MCP_JSON="${PROJECT_ROOT}/.mcp.json"
230
247
  mcp_url="${mcp_server_url%/}/mcp"
231
248
  cat > "$MCP_JSON" <<EOF
@@ -240,12 +257,12 @@ cat > "$MCP_JSON" <<EOF
240
257
  EOF
241
258
  actions+=(".mcp.json written (${mcp_url})")
242
259
 
243
- # Keep Codex's MCP config url aligned (auth header written by fabric-vibe auth refresh).
260
+ # Keep Codex's MCP config url aligned (auth config written by fabric-vibe auth refresh).
244
261
  CODEX_CONFIG="${PROJECT_ROOT}/.codex/config.toml"
245
262
  if [[ -f "$CODEX_CONFIG" ]]; then
246
263
  _codex_tmp="$(mktemp)"
247
264
  awk -v url="$mcp_url" '
248
- /^\[mcp_servers\.fabric-server\]/ { print; in_section=1; next }
265
+ /^\[mcp_servers\.("fabric-server"|fabric-server|fabric_server)\]/ { print "[mcp_servers.fabric_server]"; in_section=1; next }
249
266
  /^\[/ { in_section=0 }
250
267
  in_section && /^[[:space:]]*url[[:space:]]*=/ { print "url = \"" url "\""; next }
251
268
  { print }
@@ -257,7 +274,7 @@ fi
257
274
  echo ""
258
275
  echo "-- MCP client token"
259
276
  if fabric-vibe auth refresh; then
260
- actions+=("MCP token written to MCP client headers")
277
+ actions+=("MCP token written to MCP client auth config")
261
278
  else
262
279
  echo " MCP token refresh failed; run 'fabric-vibe auth refresh' manually." >&2
263
280
  fi
@@ -304,6 +321,6 @@ echo "Setup complete."
304
321
  for a in "${actions[@]}"; do echo "- $a"; done
305
322
  echo ""
306
323
  echo "Next: start the Fabric MCP server."
307
- echo " cd <fabric-vibecoding-settings>/server"
324
+ echo " cd <fabric-vibecoding-settings>"
308
325
  echo " docker compose up --build"
309
326
  echo "Then open Claude Code (or Codex) in this project."
@@ -198,7 +198,7 @@ def precommit(ctx: typer.Context) -> None:
198
198
 
199
199
  @auth_app.command("refresh", context_settings=_PASSTHROUGH_CONTEXT)
200
200
  def auth_refresh(ctx: typer.Context) -> None:
201
- """Generate the MCP identity key, sign your email, and update MCP configs."""
201
+ """Exchange FABRIC_MCP_API_KEY for a short-lived JWT and inject it into MCP client configs."""
202
202
  script = _require_tool_script(Path("auth/refresh.py"))
203
203
  raise typer.Exit(code=_dispatch_python(script, ctx.args))
204
204
 
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env python3
2
+ """Fetch a short-lived JWT from the MCP server using an API key.
3
+
4
+ Saves the JWT into the Claude and Codex MCP client configuration files so
5
+ Claude Code and Codex pick it up on next session load. Each run mints a fresh
6
+ token so client configs recover from server restarts, signing-secret rotation,
7
+ or server-side token-store resets.
8
+
9
+ The API key is read from FABRIC_MCP_API_KEY. If that variable is not set,
10
+ the command prompts for it interactively (input is hidden).
11
+
12
+ The auth service base URL is read from FABRIC_MCP_AUTH_URL (e.g.
13
+ https://host/server/auth). If not set, it defaults to
14
+ {MCP_SERVER_URL}/api/auth. Pass --auth-url to override at runtime.
15
+
16
+ Usage:
17
+ fabric-vibe auth refresh [--server-url https://host:port]
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import base64
23
+ import getpass
24
+ import json
25
+ import os
26
+ import re
27
+ import sys
28
+ import time
29
+ import urllib.error
30
+ import urllib.parse
31
+ import urllib.request
32
+ from datetime import datetime
33
+ from pathlib import Path
34
+
35
+ ROOT = Path.cwd()
36
+
37
+ _STATE_DIR = Path.home() / ".fabric-vibecoding"
38
+ _TOKEN_FILE = _STATE_DIR / "mcp-token.json"
39
+ _REFRESH_MARGIN = 300 # refresh when fewer than 5 min remain
40
+ _CODEX_MCP_SECTION = "mcp_servers.fabric_server"
41
+ _CODEX_MCP_HEADERS_SECTION = "mcp_servers.fabric_server.http_headers"
42
+
43
+
44
+ # ── JWT expiry helper (no external deps) ─────────────────────────────────────
45
+
46
+ def _jwt_expiry(token: str) -> float | None:
47
+ """Extract the exp claim from a JWT payload without verifying the signature."""
48
+ try:
49
+ parts = token.split(".")
50
+ if len(parts) != 3:
51
+ return None
52
+ padding = "=" * (-len(parts[1]) % 4)
53
+ payload = json.loads(base64.urlsafe_b64decode(parts[1] + padding).decode("utf-8"))
54
+ return float(payload["exp"])
55
+ except Exception:
56
+ return None
57
+
58
+
59
+ # ── Persisted token ───────────────────────────────────────────────────────────
60
+
61
+ def _load_saved_token() -> tuple[str, float] | tuple[None, None]:
62
+ if not _TOKEN_FILE.exists():
63
+ return None, None
64
+ try:
65
+ data = json.loads(_TOKEN_FILE.read_text(encoding="utf-8"))
66
+ return data.get("token"), float(data.get("expires_at", 0))
67
+ except Exception:
68
+ return None, None
69
+
70
+
71
+ def _save_token(token: str, expires_at: float) -> None:
72
+ _STATE_DIR.mkdir(parents=True, exist_ok=True)
73
+ _TOKEN_FILE.write_text(
74
+ json.dumps({"token": token, "expires_at": expires_at}), encoding="utf-8"
75
+ )
76
+ try:
77
+ os.chmod(_TOKEN_FILE, 0o600)
78
+ except OSError:
79
+ pass
80
+
81
+
82
+ # ── URL resolution ────────────────────────────────────────────────────────────
83
+
84
+ def _resolve_server_url(argv: list[str], root: Path) -> str:
85
+ """Resolve MCP server base URL from --server-url arg, env, or .mcp.json."""
86
+ if "--server-url" in argv:
87
+ idx = argv.index("--server-url")
88
+ if idx + 1 < len(argv):
89
+ return argv[idx + 1].rstrip("/")
90
+ url = os.environ.get("MCP_SERVER_URL", "").strip().rstrip("/")
91
+ if url:
92
+ return url
93
+ mcp_json = root / ".mcp.json"
94
+ if mcp_json.exists():
95
+ try:
96
+ doc = json.loads(mcp_json.read_text(encoding="utf-8-sig"))
97
+ raw = doc.get("mcpServers", {}).get("fabric-server", {}).get("url", "")
98
+ if raw:
99
+ return raw.rstrip("/").removesuffix("/mcp")
100
+ except Exception:
101
+ pass
102
+ return "http://127.0.0.1:8000"
103
+
104
+
105
+ def _resolve_auth_base_url(argv: list[str], server_url: str) -> str:
106
+ """Resolve auth service base URL.
107
+
108
+ Priority: --auth-url flag > FABRIC_MCP_AUTH_URL env var > {server_url}/api/auth.
109
+
110
+ The base URL is the path up to but not including /login or /refresh,
111
+ e.g. https://host/server/auth or https://host/api/auth.
112
+ """
113
+ if "--auth-url" in argv:
114
+ idx = argv.index("--auth-url")
115
+ if idx + 1 < len(argv):
116
+ return argv[idx + 1].rstrip("/")
117
+ url = os.environ.get("FABRIC_MCP_AUTH_URL", "").strip().rstrip("/")
118
+ if url:
119
+ return url
120
+ return f"{server_url}/api/auth"
121
+
122
+
123
+ # ── API key resolution ────────────────────────────────────────────────────────
124
+
125
+ def _prompt_api_key() -> str | None:
126
+ """Return the API key from env or an interactive prompt. Returns None to skip."""
127
+ key = os.environ.get("FABRIC_MCP_API_KEY", "").strip()
128
+ if key:
129
+ return key
130
+ print(" FABRIC_MCP_API_KEY is not set.")
131
+ try:
132
+ key = getpass.getpass(" Enter your MCP API key (empty to skip): ").strip()
133
+ except (EOFError, KeyboardInterrupt):
134
+ print()
135
+ return None
136
+ return key or None
137
+
138
+
139
+ # ── Token fetch / refresh ─────────────────────────────────────────────────────
140
+
141
+ def _post(url: str, body: bytes, headers: dict[str, str]) -> tuple[int, dict]:
142
+ req = urllib.request.Request(url, data=body, headers=headers, method="POST")
143
+ try:
144
+ with urllib.request.urlopen(req, timeout=10) as resp:
145
+ return resp.status, json.loads(resp.read().decode("utf-8"))
146
+ except urllib.error.HTTPError as exc:
147
+ try:
148
+ return exc.code, json.loads(exc.read().decode("utf-8"))
149
+ except Exception:
150
+ return exc.code, {"error": exc.reason}
151
+ except Exception as exc:
152
+ raise SystemExit(f"Cannot reach MCP server at {url}: {exc}") from exc
153
+
154
+
155
+ def _discover_token_endpoint(server_url: str) -> str | None:
156
+ """Fetch /.well-known/oauth-authorization-server and return the token_endpoint.
157
+
158
+ Returns None if the endpoint is unreachable or the server has no OAuth2 AS.
159
+ """
160
+ discovery_url = f"{server_url}/.well-known/oauth-authorization-server"
161
+ req = urllib.request.Request(discovery_url, method="GET")
162
+ try:
163
+ with urllib.request.urlopen(req, timeout=5) as resp:
164
+ if resp.status == 200:
165
+ meta = json.loads(resp.read().decode("utf-8"))
166
+ return meta.get("token_endpoint") or None
167
+ except Exception:
168
+ pass
169
+ return None
170
+
171
+
172
+ def _fetch_token(server_url: str, api_key: str) -> tuple[str, float]:
173
+ """Exchange an API key for a JWT.
174
+
175
+ Discovers the token endpoint from the server's OAuth2 AS metadata
176
+ (``/.well-known/oauth-authorization-server``) and uses the standard
177
+ client_credentials grant. Falls back to the legacy JSON endpoint
178
+ (``{server_url}/api/auth/login``) for servers that pre-date OAuth2 support.
179
+ """
180
+ # ── OAuth2 client credentials — endpoint discovered at runtime ────────────
181
+ oauth_url = _discover_token_endpoint(server_url)
182
+ if oauth_url:
183
+ form_body = urllib.parse.urlencode({
184
+ "grant_type": "client_credentials",
185
+ "client_id": api_key,
186
+ }).encode("ascii")
187
+ status, body = _post(oauth_url, form_body, {"Content-Type": "application/x-www-form-urlencoded"})
188
+ if status == 200:
189
+ token = body.get("access_token", "")
190
+ if token:
191
+ expires_in = body.get("expires_in", 3600)
192
+ expires_at = body.get("expires_at") or (time.time() + expires_in)
193
+ return token, float(expires_at)
194
+ if status not in (404, 405):
195
+ raise SystemExit(f"OAuth2 token request failed ({status}): {body.get('error', body)}")
196
+
197
+ # ── Legacy JSON endpoint (backward compat) ────────────────────────────────
198
+ legacy_url = f"{server_url}/api/auth/login"
199
+ status, body = _post(
200
+ legacy_url,
201
+ json.dumps({"api_key": api_key}).encode("utf-8"),
202
+ {"Content-Type": "application/json"},
203
+ )
204
+ if status == 404:
205
+ raise SystemExit(
206
+ f"No auth endpoint found at {server_url}.\n"
207
+ "Check FABRIC_MCP_API_KEY and MCP_SERVER_URL."
208
+ )
209
+ if status != 200:
210
+ raise SystemExit(f"Login failed ({status}): {body.get('error', body)}")
211
+ token = body.get("token", "")
212
+ if not token:
213
+ raise SystemExit(f"Server returned no token: {body}")
214
+ expires_at = body.get("expires_at") or _jwt_expiry(token) or (time.time() + 3600)
215
+ return token, float(expires_at)
216
+
217
+
218
+ def _refresh_token(auth_base_url: str, current_token: str) -> tuple[str, float] | tuple[None, None]:
219
+ """POST to {auth_base_url}/refresh. Returns (new_token, expires_at) or (None, None)."""
220
+ status, body = _post(
221
+ f"{auth_base_url}/refresh",
222
+ b"",
223
+ {"Content-Type": "application/json", "Authorization": f"Bearer {current_token}"},
224
+ )
225
+ if status != 200:
226
+ return None, None
227
+ token = body.get("token", "")
228
+ if not token:
229
+ return None, None
230
+ expires_at = body.get("expires_at") or _jwt_expiry(token) or (time.time() + 3600)
231
+ return token, float(expires_at)
232
+
233
+
234
+ # ── Config injection ──────────────────────────────────────────────────────────
235
+
236
+ def update_mcp_json(root: Path, token: str) -> None:
237
+ mcp_json = root / ".mcp.json"
238
+ if not mcp_json.exists():
239
+ print(f" .mcp.json not found at {mcp_json}; skipping", file=sys.stderr)
240
+ return
241
+ doc = json.loads(mcp_json.read_text(encoding="utf-8-sig"))
242
+ (
243
+ doc.setdefault("mcpServers", {})
244
+ .setdefault("fabric-server", {})
245
+ .setdefault("headers", {})["Authorization"]
246
+ ) = f"Bearer {token}"
247
+ mcp_json.write_text(json.dumps(doc, indent=2) + "\n", encoding="utf-8")
248
+ print(" Updated .mcp.json")
249
+
250
+
251
+ def _normalize_codex_mcp_section_names(text: str) -> str:
252
+ """Migrate old hyphenated Codex MCP ids to the stable tool namespace id."""
253
+ text = re.sub(
254
+ r'^\[mcp_servers\.(?:"fabric-server"|fabric-server)\]$',
255
+ f"[{_CODEX_MCP_SECTION}]",
256
+ text,
257
+ flags=re.MULTILINE,
258
+ )
259
+ return re.sub(
260
+ r'^\[mcp_servers\.(?:"fabric-server"|fabric-server|fabric_server)\.(?:headers|http_headers)\]$',
261
+ f"[{_CODEX_MCP_HEADERS_SECTION}]",
262
+ text,
263
+ flags=re.MULTILINE,
264
+ )
265
+
266
+
267
+ def _upsert_toml_section_key(text: str, section: str, key: str, value: str) -> str:
268
+ header = f"[{section}]"
269
+ key_line = f'{key} = "{value}"'
270
+ section_re = re.compile(
271
+ rf"(^\[{re.escape(section)}\]\r?\n)(.*?)(?=^\[|\Z)",
272
+ flags=re.MULTILINE | re.DOTALL,
273
+ )
274
+ match = section_re.search(text)
275
+ if not match:
276
+ return text.rstrip() + f"\n\n{header}\n{key_line}\n"
277
+
278
+ body = match.group(2)
279
+ if re.search(rf"^\s*{re.escape(key)}\s*=", body, flags=re.MULTILINE):
280
+ body = re.sub(
281
+ rf"^\s*{re.escape(key)}\s*=.*$",
282
+ key_line,
283
+ body,
284
+ flags=re.MULTILINE,
285
+ )
286
+ else:
287
+ body = body.rstrip() + f"\n{key_line}\n"
288
+ return text[: match.start(2)] + body + text[match.end(2):]
289
+
290
+
291
+ def update_codex_config(root: Path, token: str) -> None:
292
+ config = root / ".codex" / "config.toml"
293
+ if not config.exists():
294
+ return
295
+ text = _normalize_codex_mcp_section_names(config.read_text(encoding="utf-8"))
296
+ text = _upsert_toml_section_key(
297
+ text,
298
+ _CODEX_MCP_HEADERS_SECTION,
299
+ "Authorization",
300
+ f"Bearer {token}",
301
+ )
302
+ config.write_text(text, encoding="utf-8")
303
+ print(" Updated .codex/config.toml")
304
+
305
+
306
+ # ── Entry point ───────────────────────────────────────────────────────────────
307
+
308
+ def main(argv: list[str] | None = None) -> int:
309
+ argv = sys.argv[1:] if argv is None else argv
310
+
311
+ api_key = _prompt_api_key()
312
+ if not api_key:
313
+ print(" No API key provided — skipping auth.")
314
+ return 0
315
+
316
+ server_url = _resolve_server_url(argv, ROOT)
317
+ # Always mint a fresh token. A cached JWT can be timestamp-valid but still
318
+ # rejected after a server restart, signing-secret rotation, or JTI-store reset.
319
+ # _fetch_token tries OAuth2 client_credentials first, legacy JSON second.
320
+ token, expires_at = _fetch_token(server_url, api_key)
321
+
322
+ _save_token(token, expires_at)
323
+ update_mcp_json(ROOT, token)
324
+ update_codex_config(ROOT, token)
325
+
326
+ expiry_str = datetime.fromtimestamp(expires_at).strftime("%Y-%m-%d %H:%M:%S")
327
+ print(f"\nMCP token written to client config. Expires: {expiry_str}")
328
+ print("Reload your Claude Code / Codex session to pick up the new token.")
329
+ return 0
330
+
331
+
332
+ if __name__ == "__main__":
333
+ sys.exit(main())
@@ -19,7 +19,7 @@ fabric-vibecoding-agents = "fabric_skills_settings.cli:app"
19
19
  fabric-vibe = "fabric_skills_settings.runtime_cli:app"
20
20
 
21
21
  [dependency-groups]
22
- dev = ["pytest>=8", "Faker>=26", "mimesis>=18", "scikit-learn>=1.5", "pandas>=2", "networkx>=3.2", "rank_bm25>=0.2.2", "PyJWT>=2.8", "mcp>=1.0", "uvicorn>=0.30"]
22
+ dev = ["pytest>=8", "Faker>=26", "mimesis>=18", "scikit-learn>=1.5", "pandas>=2", "networkx>=3.2", "rank_bm25>=0.2.2", "PyJWT>=2.8", "mcp>=1.0", "uvicorn>=0.30", "sqlalchemy>=2.0"]
23
23
 
24
24
  [project.optional-dependencies]
25
25
  graph = ["networkx>=3.2", "rank_bm25>=0.2.2"]
@@ -31,11 +31,6 @@ server = [
31
31
  "rank_bm25>=0.2.2",
32
32
  "PyJWT>=2.8",
33
33
  ]
34
- # Only needed when FABRIC_MCP_API_KEYS_SOURCE=azure-blob. File mode needs nothing extra.
35
- server-azure = [
36
- "azure-storage-blob>=12.19",
37
- "azure-identity>=1.16",
38
- ]
39
34
 
40
35
  [tool.pytest.ini_options]
41
36
  testpaths = ["tests"]
@@ -12,16 +12,14 @@ COPY server /build/server
12
12
 
13
13
  # Runtime deps only. We do NOT install ms-fabric-cli — fab-dependent tools
14
14
  # live in cli/ and run on the user's laptop, not in this container.
15
- # For FABRIC_MCP_API_KEYS_SOURCE=azure-blob, also add the 'server-azure' extra
16
- # packages here: "azure-storage-blob>=12.19" "azure-identity>=1.16". They are
17
- # omitted by default to keep the file-source image lean.
18
15
  RUN uv pip install --system --no-cache \
19
16
  "mcp>=1.0" \
20
17
  "uvicorn>=0.30" \
21
18
  "pydantic>=2.7" \
22
19
  "networkx>=3.2" \
23
20
  "rank_bm25>=0.2.2" \
24
- "PyJWT>=2.8"
21
+ "PyJWT>=2.8" \
22
+ "sqlalchemy>=2.0"
25
23
 
26
24
  # Build the knowledge graph from server/content + server/skills.
27
25
  # Output: /build/dist/.graph/{graph.json, graph-bm25.pkl, materialized-graph.{html,svg}}
@@ -37,6 +35,14 @@ COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/pytho
37
35
  COPY --from=builder /build/server /app/server
38
36
  COPY --from=builder /build/dist/.graph /app/dist/.graph
39
37
 
38
+ # Create managed content directories (volume-mounted at runtime for persistence).
39
+ RUN mkdir -p /app/server/managed/skills \
40
+ /app/server/managed/content/rules \
41
+ /app/server/managed/content/skill-fixes \
42
+ /app/server/managed/content/memory && \
43
+ mkdir -p /config && \
44
+ chown -R server:server /app/server/managed /config
45
+
40
46
  WORKDIR /app
41
47
  USER server
42
48
  EXPOSE 8000