insightfactory-cli 1.0.3.dev19__tar.gz → 1.0.3.dev20__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 (65) hide show
  1. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/PKG-INFO +1 -1
  2. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/pyproject.toml +1 -1
  3. insightfactory_cli-1.0.3.dev20/src/if_cli/assets/__init__.py +1 -0
  4. insightfactory_cli-1.0.3.dev20/src/if_cli/assets/insightfactoryai-logo.svg +55 -0
  5. insightfactory_cli-1.0.3.dev20/src/if_cli/callback_page.py +215 -0
  6. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/oauth.py +5 -4
  7. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/.github/workflows/ci.yml +0 -0
  8. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/.github/workflows/claude.yml +0 -0
  9. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/.github/workflows/release.yml +0 -0
  10. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/.gitignore +0 -0
  11. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/.python-version +0 -0
  12. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/AGENTS.md +0 -0
  13. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/CLAUDE.md +0 -0
  14. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/LICENSE +0 -0
  15. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/README.md +0 -0
  16. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/__init__.py +0 -0
  17. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/__main__.py +0 -0
  18. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/cache.py +0 -0
  19. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/cli.py +0 -0
  20. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/colour.py +0 -0
  21. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/__init__.py +0 -0
  22. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/api.py +0 -0
  23. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/config.py +0 -0
  24. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/login.py +0 -0
  25. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/logout.py +0 -0
  26. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/mcp.py +0 -0
  27. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/profiles.py +0 -0
  28. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/set_token.py +0 -0
  29. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/commands/token.py +0 -0
  30. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/config.py +0 -0
  31. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/constants.py +0 -0
  32. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/http.py +0 -0
  33. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/main.py +0 -0
  34. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/router/__init__.py +0 -0
  35. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/router/catalog.py +0 -0
  36. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/router/policy.py +0 -0
  37. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/router/server.py +0 -0
  38. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/router/upstream.py +0 -0
  39. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/src/if_cli/runtime.py +0 -0
  40. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/__init__.py +0 -0
  41. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/cache_writer.py +0 -0
  42. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/conftest.py +0 -0
  43. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/helpers.py +0 -0
  44. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/servers.py +0 -0
  45. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_api.py +0 -0
  46. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_api_command.py +0 -0
  47. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_cache.py +0 -0
  48. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_cli.py +0 -0
  49. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_config.py +0 -0
  50. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_config_command.py +0 -0
  51. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_login.py +0 -0
  52. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_mcp_command.py +0 -0
  53. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_oauth.py +0 -0
  54. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_oauth_flow.py +0 -0
  55. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_oauth_force_refresh.py +0 -0
  56. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_profiles.py +0 -0
  57. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_programmatic_api.py +0 -0
  58. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_router_catalog.py +0 -0
  59. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_router_policy.py +0 -0
  60. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_router_server.py +0 -0
  61. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_router_upstream.py +0 -0
  62. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_runtime.py +0 -0
  63. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_set_token.py +0 -0
  64. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/tests/test_token.py +0 -0
  65. {insightfactory_cli-1.0.3.dev19 → insightfactory_cli-1.0.3.dev20}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: insightfactory-cli
3
- Version: 1.0.3.dev19
3
+ Version: 1.0.3.dev20
4
4
  Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
5
5
  Project-URL: Homepage, https://insightfactory.ai
6
6
  Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "insightfactory-cli"
3
- version = "1.0.3.dev19"
3
+ version = "1.0.3.dev20"
4
4
  description = "Profile-based authentication CLI for the InsightFactory Interfaces API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1 @@
1
+ """Static files shipped with the CLI."""
@@ -0,0 +1,55 @@
1
+ <svg class="logo" role="img" aria-label="insightfactory.ai" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 286.67 62.53">
2
+ <defs>
3
+ <style>
4
+ .iflogo-1 {
5
+ fill: #fab314;
6
+ }
7
+ .iflogo-2 {
8
+ fill: #f8e86c;
9
+ }
10
+ .iflogo-3 {
11
+ fill: #fad121;
12
+ }
13
+ .iflogo-4 {
14
+ fill: #1f252e;
15
+ }
16
+ </style>
17
+ </defs>
18
+ <g id="ARTWORK">
19
+ <g>
20
+ <g>
21
+ <polygon class="iflogo-4" points="32.49 37.52 32.5 37.53 32.49 37.52 32.49 37.52"/>
22
+ <polygon class="iflogo-4" points="21.66 18.76 0 31.27 21.66 43.78 21.66 18.76"/>
23
+ <g>
24
+ <polygon class="iflogo-4" points="43.32 31.27 43.32 43.78 54.15 37.52 43.32 31.27"/>
25
+ <polygon class="iflogo-4" points="43.32 43.78 43.32 56.28 54.15 62.53 64.98 56.28 43.32 43.78"/>
26
+ </g>
27
+ <polygon class="iflogo-2" points="21.66 6.26 32.49 12.51 43.32 6.26 32.48 0 21.66 6.26"/>
28
+ <polygon class="iflogo-3" points="32.49 12.51 32.49 12.51 21.66 6.26 21.66 18.76 21.66 18.76 21.66 43.78 32.49 50.03 32.49 37.52 32.49 12.51"/>
29
+ <g>
30
+ <polygon class="iflogo-1" points="32.49 12.51 32.5 25.02 43.32 18.76 43.32 6.26 32.49 12.51"/>
31
+ <polygon class="iflogo-1" points="43.32 56.28 43.32 43.78 43.32 31.27 32.49 37.52 32.49 50.03 43.32 56.28"/>
32
+ </g>
33
+ </g>
34
+ <g>
35
+ <path class="iflogo-4" d="m75.28,23.74c-.81,0-1.47-.27-1.99-.8-.52-.53-.77-1.17-.77-1.91s.26-1.41.77-1.93,1.18-.78,1.99-.78c.76,0,1.41.26,1.94.78.53.52.79,1.17.79,1.93s-.26,1.38-.79,1.91c-.53.53-1.17.8-1.94.8Zm-2.19,2.23h4.31v17.39h-4.31v-17.39Z"/>
36
+ <path class="iflogo-4" d="m79.46,25.97h3.91l.3,1.77c.54-.63,1.2-1.12,1.97-1.48.78-.36,1.61-.54,2.51-.54,1.33,0,2.46.29,3.39.87.93.58,1.65,1.42,2.16,2.52.51,1.1.76,2.46.76,4.09v10.16h-4.35v-9.57c0-1.34-.26-2.35-.78-3.03s-1.29-1.01-2.33-1.01-1.86.34-2.41,1.03c-.55.68-.83,1.7-.83,3.04v9.53h-4.31v-17.39Z"/>
37
+ <path class="iflogo-4" d="m100.08,37.69c.13.58.36,1.02.69,1.32.33.3.7.52,1.11.64.42.13.84.19,1.26.19.79,0,1.4-.16,1.85-.47.45-.31.67-.71.67-1.2,0-.42-.16-.75-.47-.99-.31-.24-.71-.43-1.2-.57-.48-.14-.99-.27-1.53-.38-.61-.16-1.25-.34-1.94-.54-.69-.2-1.33-.48-1.94-.85-.61-.37-1.11-.85-1.5-1.44-.39-.59-.59-1.34-.59-2.24,0-1.09.28-2.03.84-2.83.56-.8,1.35-1.43,2.38-1.9s2.21-.7,3.56-.7c1.59,0,2.94.35,4.04,1.04,1.1.7,1.9,1.69,2.39,2.99l-3.78,1.11c-.13-.32-.34-.59-.61-.8s-.59-.36-.96-.45c-.37-.09-.75-.14-1.13-.14-.65,0-1.19.13-1.62.4-.43.27-.64.63-.64,1.1,0,.28.08.5.24.68s.38.32.66.43c.28.12.61.22.98.31.37.09.78.2,1.23.31.7.19,1.4.4,2.11.64.71.24,1.36.55,1.96.92.59.37,1.07.87,1.43,1.5.36.63.56,1.42.61,2.37,0,1.02-.28,1.94-.84,2.77s-1.37,1.48-2.43,1.96c-1.06.49-2.34.73-3.84.73-1.75,0-3.24-.38-4.47-1.15s-2.03-1.96-2.41-3.58l3.88-1.18Z"/>
38
+ <path class="iflogo-4" d="m114.61,23.74c-.81,0-1.47-.27-1.99-.8-.52-.53-.77-1.17-.77-1.91s.26-1.41.77-1.93,1.18-.78,1.99-.78c.76,0,1.41.26,1.94.78.53.52.79,1.17.79,1.93s-.26,1.38-.79,1.91c-.53.53-1.17.8-1.94.8Zm-2.19,2.23h4.31v17.39h-4.31v-17.39Z"/>
39
+ <path class="iflogo-4" d="m131.9,25.97h3.91v14.82c0,1.97-.47,3.67-1.42,5.1-.94,1.43-2.31,2.52-4.09,3.29-1.79.76-3.93,1.15-6.42,1.15l-.67-3.76c2.72-.05,4.78-.54,6.18-1.5s2.11-2.32,2.11-4.1v-.07l.57-.21c-.54.81-1.28,1.5-2.22,2.07s-2.07.85-3.37.85c-1.48,0-2.79-.37-3.91-1.11-1.12-.74-2.01-1.78-2.65-3.11-.64-1.33-.96-2.91-.96-4.71s.32-3.39.96-4.73c.64-1.34,1.52-2.38,2.65-3.11,1.12-.73,2.43-1.1,3.91-1.1.76,0,1.48.11,2.14.33.66.22,1.25.51,1.75.87.5.36.93.76,1.26,1.2l.27-2.16Zm-8.32,8.7c0,1.46.37,2.64,1.09,3.53.73.89,1.7,1.34,2.92,1.34.7,0,1.34-.19,1.92-.57.58-.38,1.06-.94,1.43-1.69.37-.74.56-1.61.56-2.61s-.19-1.92-.56-2.64c-.37-.72-.85-1.27-1.45-1.65-.59-.38-1.23-.57-1.9-.57-1.21,0-2.19.45-2.92,1.34-.73.89-1.09,2.07-1.09,3.53Z"/>
40
+ <path class="iflogo-4" d="m138.5,18.32h4.31v9.36c.52-.58,1.16-1.05,1.94-1.41.78-.36,1.65-.54,2.61-.54,1.28,0,2.39.29,3.34.87.94.58,1.68,1.42,2.21,2.52.53,1.1.79,2.46.79,4.09v10.16h-4.55v-9.57c0-1.34-.26-2.35-.78-3.03s-1.29-1.01-2.33-1.01-1.86.37-2.41,1.1c-.55.73-.83,1.77-.83,3.11v9.39h-4.31v-25.04Z"/>
41
+ <path class="iflogo-4" d="m157.01,25.97v-4.87h4.35v4.87h3.74v3.93h-3.74v7.06c0,.9.19,1.54.57,1.91.38.37.86.57,1.43.59.57.02,1.15-.01,1.74-.1v3.83c-1.03.23-2.04.3-3.02.21-.98-.09-1.85-.37-2.61-.82-.76-.45-1.36-1.14-1.8-2.07-.44-.93-.66-2.11-.66-3.55v-7.06h-2.39v-3.93h2.39Z"/>
42
+ <path class="iflogo-4" d="m170.42,23.92c0-1.27.2-2.35.61-3.23s1.05-1.52,1.94-1.93c.89-.41,2.07-.55,3.56-.43v2.33c-1.35-.12-2.28.11-2.8.68s-.78,1.43-.78,2.59v2.05h3.71v2.43h-3.71v14.96h-2.53v-14.96h-2.36v-2.43h2.36v-2.05Z"/>
43
+ <path class="iflogo-4" d="m188.25,43.36l-.2-3.13c-.47,1.07-1.17,1.9-2.09,2.49-.92.59-2.09.89-3.5.89-1.21,0-2.25-.2-3.1-.59-.85-.39-1.51-.96-1.97-1.69-.46-.73-.69-1.59-.69-2.59,0-1.46.53-2.66,1.58-3.6,1.06-.94,2.58-1.51,4.58-1.72l5.19-.59v-1.39c0-.97-.35-1.76-1.04-2.35s-1.64-.89-2.83-.89-2.14.28-2.92.83c-.78.56-1.31,1.36-1.6,2.4l-2.29-.83c.45-1.53,1.28-2.72,2.48-3.58,1.2-.86,2.67-1.29,4.4-1.29,2,0,3.56.53,4.67,1.58,1.11,1.06,1.67,2.49,1.67,4.3v11.76h-2.33Zm-.2-8.31l-5.22.63c-1.12.14-1.98.46-2.56.96-.58.5-.88,1.15-.88,1.96s.29,1.46.86,1.95c.57.49,1.4.73,2.48.73,1.24,0,2.25-.25,3.03-.77.79-.51,1.37-1.2,1.74-2.07.37-.87.56-1.81.56-2.83v-.56Z"/>
44
+ <path class="iflogo-4" d="m201.02,43.61c-1.64,0-3.08-.37-4.31-1.11-1.24-.74-2.2-1.78-2.88-3.11-.69-1.33-1.03-2.88-1.03-4.64s.34-3.36,1.03-4.71,1.65-2.41,2.88-3.17c1.24-.75,2.67-1.13,4.31-1.13,1.73,0,3.25.41,4.55,1.23,1.3.82,2.25,1.97,2.83,3.43l-2.33.83c-.34-.93-.95-1.66-1.84-2.21-.89-.54-1.92-.82-3.08-.82s-2.14.27-2.98.8-1.49,1.29-1.96,2.26c-.46.97-.69,2.12-.69,3.44,0,1.95.51,3.51,1.52,4.68s2.36,1.76,4.04,1.76c1.15,0,2.17-.27,3.07-.8.9-.53,1.56-1.25,1.99-2.16l2.29.83c-.61,1.44-1.56,2.56-2.86,3.38-1.3.81-2.82,1.22-4.55,1.22Z"/>
45
+ <path class="iflogo-4" d="m212.07,25.97v-4.87h2.53v4.87h4.48v2.43h-4.48v9.36c0,1.07.22,1.84.66,2.33.44.49,1,.78,1.68.87.69.09,1.4.09,2.14,0v2.26c-.88.21-1.74.27-2.58.17-.84-.09-1.6-.35-2.26-.78-.66-.43-1.19-1.04-1.58-1.83-.39-.79-.59-1.8-.59-3.03v-9.36h-2.53v-2.43h2.53Z"/>
46
+ <path class="iflogo-4" d="m228.49,43.61c-1.6,0-3.02-.36-4.28-1.08-1.26-.72-2.25-1.74-2.97-3.08-.72-1.33-1.08-2.92-1.08-4.75s.37-3.46,1.1-4.8c.73-1.34,1.73-2.38,3-3.1,1.27-.72,2.7-1.08,4.3-1.08s3.03.36,4.3,1.08c1.27.72,2.27,1.75,3,3.08.73,1.33,1.1,2.92,1.1,4.75s-.37,3.46-1.11,4.8-1.75,2.38-3.03,3.1-2.72,1.08-4.31,1.08Zm-.03-2.47c.99,0,1.92-.23,2.8-.68.88-.45,1.59-1.16,2.14-2.12.55-.96.83-2.19.83-3.67s-.27-2.74-.81-3.69c-.54-.95-1.24-1.65-2.11-2.1s-1.79-.68-2.78-.68-1.91.23-2.76.68-1.54,1.16-2.07,2.12c-.53.96-.79,2.19-.79,3.67s.26,2.74.78,3.69c.52.95,1.2,1.65,2.04,2.1.84.45,1.76.68,2.75.68Z"/>
47
+ <path class="iflogo-4" d="m238.93,25.97h2.33l.13,2.54c.45-.63.99-1.16,1.63-1.6.64-.44,1.35-.75,2.12-.92.77-.17,1.6-.18,2.48-.02v2.61c-.94-.18-1.79-.18-2.54,0-.75.19-1.4.53-1.94,1.04-.54.51-.96,1.15-1.25,1.93-.29.78-.44,1.68-.44,2.7v9.11h-2.53v-17.39Z"/>
48
+ <path class="iflogo-4" d="m262.19,25.97h2.63l-9.64,24.35h-2.43l2.87-7.2-6.81-17.15h2.76l5.39,14.05,5.22-14.05Z"/>
49
+ <path class="iflogo-4" d="m263.09,43.52c-.46,0-.84-.16-1.15-.5-.31-.33-.46-.73-.46-1.21s.15-.88.46-1.21c.31-.33.69-.5,1.15-.5s.82.17,1.14.5c.32.33.48.73.48,1.21s-.16.88-.48,1.21c-.32.33-.7.5-1.14.5Z"/>
50
+ <path class="iflogo-4" d="m278.25,43.36l-.2-3.13c-.47,1.07-1.17,1.9-2.09,2.49-.92.59-2.09.89-3.5.89-1.21,0-2.25-.2-3.1-.59-.85-.39-1.51-.96-1.97-1.69-.46-.73-.69-1.59-.69-2.59,0-1.46.53-2.66,1.58-3.6,1.06-.94,2.58-1.51,4.58-1.72l5.19-.59v-1.39c0-.97-.35-1.76-1.04-2.35s-1.64-.89-2.83-.89-2.14.28-2.92.83c-.78.56-1.31,1.36-1.6,2.4l-2.29-.83c.45-1.53,1.28-2.72,2.48-3.58,1.2-.86,2.67-1.29,4.4-1.29,2,0,3.56.53,4.67,1.58,1.11,1.06,1.67,2.49,1.67,4.3v11.76h-2.33Zm-.2-8.31l-5.22.63c-1.12.14-1.98.46-2.56.96-.58.5-.88,1.15-.88,1.96s.29,1.46.86,1.95c.57.49,1.4.73,2.48.73,1.24,0,2.25-.25,3.03-.77.79-.51,1.37-1.2,1.74-2.07.37-.87.56-1.81.56-2.83v-.56Z"/>
51
+ <path class="iflogo-4" d="m284.92,22.49c-.52,0-.94-.17-1.28-.52-.34-.35-.51-.78-.51-1.29s.17-.97.51-1.32.76-.52,1.28-.52c.47,0,.88.17,1.23.52.35.35.52.79.52,1.32s-.17.94-.52,1.29c-.35.35-.76.52-1.23.52Zm-1.28,3.48h2.49v17.39h-2.49v-17.39Z"/>
52
+ </g>
53
+ </g>
54
+ </g>
55
+ </svg>
@@ -0,0 +1,215 @@
1
+ """Branded HTML for the loopback OAuth callback tab.
2
+
3
+ This tab is the frame straight after the Auth0 Universal Login widget, so it
4
+ copies that theme rather than the Interfaces app. Every value here mirrors
5
+ ``auth0_branding_theme.insight_factory`` in
6
+ ``terraform-Insightfactory-infrastructure/Auth0/modules/auth0/branding.tf``, and
7
+ the comments name the Terraform attribute each one came from. Change them together.
8
+
9
+ The page is self-contained: the logo is inline SVG and the CSS is inline, so it
10
+ renders from the loopback server with no network access beyond the webfont.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from functools import lru_cache
16
+ from importlib.resources import files
17
+
18
+
19
+ @lru_cache(maxsize=1)
20
+ def logo_svg() -> str:
21
+ """The wordmark Auth0 serves from the CDN, shipped with the package.
22
+
23
+ Auth0 loads it from cdn.insightfactory.ai. We inline the same file so the
24
+ callback page does not depend on the CDN being reachable.
25
+ """
26
+ return files("if_cli.assets").joinpath("insightfactoryai-logo.svg").read_text(encoding="utf-8")
27
+
28
+
29
+ STYLES = """\
30
+ @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap");
31
+
32
+ :root {
33
+ --page-background: #20252e; /* page_background.background_color */
34
+ --widget-background: #ffffff; /* colors.widget_background */
35
+ --header: #20252e; /* colors.header */
36
+ --body-text: #20252e; /* colors.body_text */
37
+ --icons: #65676e; /* colors.icons */
38
+ --input-border: #c9cace; /* colors.input_border */
39
+ --success: #13a688; /* colors.success */
40
+ --error: #d03c38; /* colors.error */
41
+ --success-soft: rgba(19, 166, 136, 0.12);
42
+ --error-soft: rgba(208, 60, 56, 0.12);
43
+ }
44
+
45
+ * {
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ body {
50
+ margin: 0;
51
+ min-height: 100vh;
52
+ display: flex;
53
+ align-items: center; /* page_layout: center */
54
+ justify-content: center;
55
+ padding: 24px;
56
+ background: var(--page-background);
57
+ color: var(--body-text);
58
+ font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
59
+ font-size: 16px; /* fonts.reference_text_size */
60
+ font-weight: 300;
61
+ line-height: 1.5;
62
+ -webkit-font-smoothing: antialiased;
63
+ }
64
+
65
+ main {
66
+ width: 100%;
67
+ max-width: 400px;
68
+ padding: 40px 32px 32px;
69
+ border-radius: 8px; /* borders.widget_corner_radius */
70
+ background: var(--widget-background);
71
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28); /* borders.show_widget_shadow */
72
+ text-align: center; /* widget.header_text_alignment */
73
+ }
74
+
75
+ .logo {
76
+ height: 68px; /* widget.logo_height */
77
+ width: auto;
78
+ max-width: 100%;
79
+ display: block;
80
+ margin: 0 auto 24px; /* widget.logo_position: center */
81
+ }
82
+
83
+ .pill {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: 7px;
87
+ margin-bottom: 16px;
88
+ padding: 4px 12px 4px 10px;
89
+ border-radius: 999px;
90
+ background: var(--pill-soft);
91
+ color: var(--pill);
92
+ font-size: 14px;
93
+ font-weight: 500;
94
+ }
95
+
96
+ .dot {
97
+ width: 7px;
98
+ height: 7px;
99
+ border-radius: 50%;
100
+ background: currentColor;
101
+ }
102
+
103
+ h1 {
104
+ margin: 0 0 10px;
105
+ color: var(--header); /* colors.header */
106
+ font-size: 24px; /* fonts.title: 150% of 16px */
107
+ font-weight: 300; /* fonts.title.bold = false */
108
+ line-height: 1.25;
109
+ }
110
+
111
+ p {
112
+ margin: 0;
113
+ color: var(--icons);
114
+ font-size: 14px; /* fonts.subtitle: 87.5% of 16px */
115
+ font-weight: 400;
116
+ }
117
+
118
+ code {
119
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
120
+ font-size: 13px;
121
+ color: var(--body-text);
122
+ }
123
+
124
+ pre {
125
+ margin: 20px 0 0;
126
+ padding: 12px 14px;
127
+ border: 1px solid var(--input-border); /* colors.input_border */
128
+ border-radius: 3px; /* borders.input_border_radius */
129
+ color: var(--body-text);
130
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
131
+ font-size: 13px;
132
+ text-align: left;
133
+ white-space: pre-wrap;
134
+ word-break: break-word;
135
+ }
136
+ """
137
+
138
+ _TEMPLATE = """\
139
+ <!DOCTYPE html>
140
+ <html lang="en">
141
+ <head>
142
+ <meta charset="utf-8">
143
+ <meta name="viewport" content="width=device-width, initial-scale=1">
144
+ <title>{title} | insightfactory.ai</title>
145
+ <style>
146
+ {styles}
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <main style="--pill: var({pill_colour}); --pill-soft: var({pill_soft});">
151
+ {logo}
152
+ <span class="pill"><span class="dot"></span>{pill}</span>
153
+ <h1>{heading}</h1>
154
+ <p>{message}</p>
155
+ {detail}
156
+ </main>
157
+ </body>
158
+ </html>
159
+ """
160
+
161
+
162
+ def render(
163
+ *,
164
+ title: str,
165
+ pill: str,
166
+ heading: str,
167
+ message: str,
168
+ detail: str = "",
169
+ ok: bool = True,
170
+ ) -> bytes:
171
+ """Render one callback page, ready to write to the socket."""
172
+ return _TEMPLATE.format(
173
+ title=title,
174
+ styles=STYLES,
175
+ logo=logo_svg(),
176
+ pill=pill,
177
+ pill_colour="--success" if ok else "--error",
178
+ pill_soft="--success-soft" if ok else "--error-soft",
179
+ heading=heading,
180
+ message=message,
181
+ detail=detail,
182
+ ).encode("utf-8")
183
+
184
+
185
+ def success_page() -> bytes:
186
+ return render(
187
+ title="Login complete",
188
+ pill="Connected",
189
+ heading="Authentication successful",
190
+ message="You can close this tab and return to the terminal.",
191
+ )
192
+
193
+
194
+ def failure_page(error: str, description: str) -> bytes:
195
+ return render(
196
+ title="Login failed",
197
+ pill="Not connected",
198
+ heading="Authentication failed",
199
+ message="Close this tab and run <code>if-cli login</code> again.",
200
+ detail=f"<pre>{error}: {description}</pre>" if error or description else "",
201
+ ok=False,
202
+ )
203
+
204
+
205
+ def state_mismatch_page() -> bytes:
206
+ return render(
207
+ title="Login callback ignored",
208
+ pill="Not connected",
209
+ heading="Callback ignored",
210
+ message=(
211
+ "The callback state did not match the one this login started with, "
212
+ "so it was discarded. Close this tab and run <code>if-cli login</code> again."
213
+ ),
214
+ ok=False,
215
+ )
@@ -18,6 +18,7 @@ from if_cli.cache import (
18
18
  load_cache,
19
19
  store_tokens,
20
20
  )
21
+ from if_cli.callback_page import failure_page, state_mismatch_page, success_page
21
22
  from if_cli.config import Profile, meaningful
22
23
  from if_cli.constants import DEFAULT_SCOPE, LOOPBACK_HOST
23
24
  from if_cli.http import DEFAULT_REQUEST_TIMEOUT_S, fetch_with_timeout, validate_http_url
@@ -187,7 +188,7 @@ class _CallbackServer:
187
188
  self.send_header("Content-Type", "text/html; charset=utf-8")
188
189
  self.send_header("Connection", "close")
189
190
  self.end_headers()
190
- self.wfile.write(b"<h2>Login callback ignored because its state did not match.</h2>")
191
+ self.wfile.write(state_mismatch_page())
191
192
  return
192
193
  code = params.get("code")
193
194
  self.send_response(200)
@@ -195,12 +196,12 @@ class _CallbackServer:
195
196
  self.send_header("Connection", "close")
196
197
  self.end_headers()
197
198
  if code:
198
- body = "<h2>Login complete — you can close this tab and return to the terminal.</h2>"
199
+ body = success_page()
199
200
  else:
200
201
  error = escape_html(params.get("error") or "")
201
202
  description = escape_html(params.get("error_description") or "")
202
- body = f"<h2>Login failed.</h2><pre>{error}: {description}</pre>"
203
- self.wfile.write(body.encode("utf-8"))
203
+ body = failure_page(error, description)
204
+ self.wfile.write(body)
204
205
  callback.result = params
205
206
 
206
207
  return Handler