bevor-cli 0.1.0__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 (101) hide show
  1. bevor_cli-0.1.0/.gitignore +60 -0
  2. bevor_cli-0.1.0/PKG-INFO +14 -0
  3. bevor_cli-0.1.0/README.md +113 -0
  4. bevor_cli-0.1.0/bevor_cli/__init__.py +0 -0
  5. bevor_cli-0.1.0/bevor_cli/annotations/__init__.py +0 -0
  6. bevor_cli-0.1.0/bevor_cli/annotations/confirmations.py +16 -0
  7. bevor_cli-0.1.0/bevor_cli/annotations/query.py +25 -0
  8. bevor_cli-0.1.0/bevor_cli/apps/agent.py +10 -0
  9. bevor_cli-0.1.0/bevor_cli/apps/analyses.py +47 -0
  10. bevor_cli-0.1.0/bevor_cli/apps/chat.py +16 -0
  11. bevor_cli-0.1.0/bevor_cli/apps/codes.py +18 -0
  12. bevor_cli-0.1.0/bevor_cli/apps/findings.py +22 -0
  13. bevor_cli-0.1.0/bevor_cli/apps/main.py +61 -0
  14. bevor_cli-0.1.0/bevor_cli/apps/nodes.py +17 -0
  15. bevor_cli-0.1.0/bevor_cli/apps/projects.py +14 -0
  16. bevor_cli-0.1.0/bevor_cli/apps/sessions.py +13 -0
  17. bevor_cli-0.1.0/bevor_cli/apps/workspaces.py +16 -0
  18. bevor_cli-0.1.0/bevor_cli/client.py +157 -0
  19. bevor_cli-0.1.0/bevor_cli/commands/__init__.py +0 -0
  20. bevor_cli-0.1.0/bevor_cli/commands/agent/__init__.py +0 -0
  21. bevor_cli-0.1.0/bevor_cli/commands/agent/materialze_cmd.py +65 -0
  22. bevor_cli-0.1.0/bevor_cli/commands/agent/setup_cmd.py +18 -0
  23. bevor_cli-0.1.0/bevor_cli/commands/analyses/__init__.py +0 -0
  24. bevor_cli-0.1.0/bevor_cli/commands/analyses/active.py +42 -0
  25. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/__init__.py +0 -0
  26. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/get.py +39 -0
  27. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/list.py +64 -0
  28. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/messages.py +41 -0
  29. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/new.py +44 -0
  30. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/resume.py +43 -0
  31. bevor_cli-0.1.0/bevor_cli/commands/analyses/chats/session.py +295 -0
  32. bevor_cli-0.1.0/bevor_cli/commands/analyses/child.py +81 -0
  33. bevor_cli-0.1.0/bevor_cli/commands/analyses/commit.py +49 -0
  34. bevor_cli-0.1.0/bevor_cli/commands/analyses/create.py +105 -0
  35. bevor_cli-0.1.0/bevor_cli/commands/analyses/delete.py +65 -0
  36. bevor_cli-0.1.0/bevor_cli/commands/analyses/diff.py +87 -0
  37. bevor_cli-0.1.0/bevor_cli/commands/analyses/entrypoints.py +56 -0
  38. bevor_cli-0.1.0/bevor_cli/commands/analyses/findings.py +129 -0
  39. bevor_cli-0.1.0/bevor_cli/commands/analyses/fork.py +47 -0
  40. bevor_cli-0.1.0/bevor_cli/commands/analyses/list.py +85 -0
  41. bevor_cli-0.1.0/bevor_cli/commands/analyses/merge.py +46 -0
  42. bevor_cli-0.1.0/bevor_cli/commands/analyses/retry.py +77 -0
  43. bevor_cli-0.1.0/bevor_cli/commands/analyses/run.py +87 -0
  44. bevor_cli-0.1.0/bevor_cli/commands/analyses/select.py +85 -0
  45. bevor_cli-0.1.0/bevor_cli/commands/analyses/set_head.py +45 -0
  46. bevor_cli-0.1.0/bevor_cli/commands/analyses/transitions.py +90 -0
  47. bevor_cli-0.1.0/bevor_cli/commands/analyses/view.py +94 -0
  48. bevor_cli-0.1.0/bevor_cli/commands/auth/__init__.py +7 -0
  49. bevor_cli-0.1.0/bevor_cli/commands/auth/login.py +135 -0
  50. bevor_cli-0.1.0/bevor_cli/commands/auth/logout.py +23 -0
  51. bevor_cli-0.1.0/bevor_cli/commands/auth/whoami.py +21 -0
  52. bevor_cli-0.1.0/bevor_cli/commands/codes/__init__.py +0 -0
  53. bevor_cli-0.1.0/bevor_cli/commands/codes/list.py +90 -0
  54. bevor_cli-0.1.0/bevor_cli/commands/codes/push.py +76 -0
  55. bevor_cli-0.1.0/bevor_cli/commands/codes/select.py +93 -0
  56. bevor_cli-0.1.0/bevor_cli/commands/codes/view.py +80 -0
  57. bevor_cli-0.1.0/bevor_cli/commands/context/__init__.py +92 -0
  58. bevor_cli-0.1.0/bevor_cli/commands/findings/__init__.py +0 -0
  59. bevor_cli-0.1.0/bevor_cli/commands/findings/acknowledge.py +28 -0
  60. bevor_cli-0.1.0/bevor_cli/commands/findings/add.py +236 -0
  61. bevor_cli-0.1.0/bevor_cli/commands/findings/bulk.py +110 -0
  62. bevor_cli-0.1.0/bevor_cli/commands/findings/delete.py +28 -0
  63. bevor_cli-0.1.0/bevor_cli/commands/findings/edit.py +109 -0
  64. bevor_cli-0.1.0/bevor_cli/commands/findings/get.py +62 -0
  65. bevor_cli-0.1.0/bevor_cli/commands/findings/interactive.py +329 -0
  66. bevor_cli-0.1.0/bevor_cli/commands/findings/list.py +108 -0
  67. bevor_cli-0.1.0/bevor_cli/commands/findings/revert.py +28 -0
  68. bevor_cli-0.1.0/bevor_cli/commands/graph/__init__.py +0 -0
  69. bevor_cli-0.1.0/bevor_cli/commands/graph/call_chain.py +48 -0
  70. bevor_cli-0.1.0/bevor_cli/commands/graph/edges.py +59 -0
  71. bevor_cli-0.1.0/bevor_cli/commands/graph/interactive.py +207 -0
  72. bevor_cli-0.1.0/bevor_cli/commands/graph/renderer.py +135 -0
  73. bevor_cli-0.1.0/bevor_cli/commands/link/__init__.py +52 -0
  74. bevor_cli-0.1.0/bevor_cli/commands/link/helpers.py +26 -0
  75. bevor_cli-0.1.0/bevor_cli/commands/nodes/__init__.py +0 -0
  76. bevor_cli-0.1.0/bevor_cli/commands/nodes/get.py +52 -0
  77. bevor_cli-0.1.0/bevor_cli/commands/nodes/list.py +75 -0
  78. bevor_cli-0.1.0/bevor_cli/commands/nodes/source.py +40 -0
  79. bevor_cli-0.1.0/bevor_cli/commands/projects/__init__.py +0 -0
  80. bevor_cli-0.1.0/bevor_cli/commands/projects/create.py +54 -0
  81. bevor_cli-0.1.0/bevor_cli/commands/projects/list.py +57 -0
  82. bevor_cli-0.1.0/bevor_cli/commands/projects/select.py +75 -0
  83. bevor_cli-0.1.0/bevor_cli/commands/projects/view.py +49 -0
  84. bevor_cli-0.1.0/bevor_cli/commands/sessions/__init__.py +0 -0
  85. bevor_cli-0.1.0/bevor_cli/commands/sessions/list.py +36 -0
  86. bevor_cli-0.1.0/bevor_cli/commands/sessions/revoke.py +35 -0
  87. bevor_cli-0.1.0/bevor_cli/commands/workspaces/__init__.py +0 -0
  88. bevor_cli-0.1.0/bevor_cli/commands/workspaces/list.py +50 -0
  89. bevor_cli-0.1.0/bevor_cli/commands/workspaces/pick.py +20 -0
  90. bevor_cli-0.1.0/bevor_cli/commands/workspaces/select.py +68 -0
  91. bevor_cli-0.1.0/bevor_cli/commands/workspaces/view.py +48 -0
  92. bevor_cli-0.1.0/bevor_cli/configuration/creds.py +52 -0
  93. bevor_cli-0.1.0/bevor_cli/configuration/settings.py +54 -0
  94. bevor_cli-0.1.0/bevor_cli/configuration/state.py +113 -0
  95. bevor_cli-0.1.0/bevor_cli/findings_yaml.py +291 -0
  96. bevor_cli-0.1.0/bevor_cli/main.py +33 -0
  97. bevor_cli-0.1.0/bevor_cli/output.py +69 -0
  98. bevor_cli-0.1.0/bevor_cli/select.py +64 -0
  99. bevor_cli-0.1.0/bevor_cli/settings.py +4 -0
  100. bevor_cli-0.1.0/cli-hierarchy.py +26 -0
  101. bevor_cli-0.1.0/pyproject.toml +26 -0
@@ -0,0 +1,60 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Node (railway.ts)
9
+ node_modules/
10
+
11
+ # uv / virtualenvs
12
+ .venv/
13
+
14
+ # tmp
15
+ services/rust_contracts/rust_contracts
16
+
17
+ # Tooling caches
18
+ .ruff_cache/
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+ .coverage
22
+ .coverage.*
23
+ htmlcov/
24
+
25
+ # Env files
26
+ .env
27
+ .env.*
28
+ !.env.example
29
+
30
+ # Terraform
31
+ .terraform/
32
+ *.tfstate
33
+ *.tfstate.*
34
+ *.tfvars
35
+ *.tfvars.json
36
+ !*.tfvars.example
37
+ crash.log
38
+ crash.*.log
39
+
40
+ # Editors / OS
41
+ .vscode/
42
+ .idea/
43
+ .zed/
44
+ .DS_Store
45
+
46
+ # Scratch
47
+ *.ipynb
48
+ mock.py
49
+ tmp/
50
+ logs/
51
+ *.log
52
+
53
+ # Local development / tests
54
+ .local/
55
+ .test-repo-cache/
56
+ tests/integration/fixtures/real_repos/*
57
+ !tests/integration/fixtures/real_repos/config.py
58
+
59
+ # Bevor
60
+ .bevor
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.5
2
+ Name: bevor-cli
3
+ Version: 0.1.0
4
+ Summary: Bevor CLI tool for security analysis and graph inspection
5
+ Classifier: Development Status :: 3 - Alpha
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: bevor-sdk==0.1.0
8
+ Requires-Dist: click~=8.1
9
+ Requires-Dist: httpx~=0.28
10
+ Requires-Dist: pydantic~=2.12
11
+ Requires-Dist: pyyaml~=6.0
12
+ Requires-Dist: questionary~=2.0
13
+ Requires-Dist: rich~=15.0
14
+ Requires-Dist: typer~=0.26
@@ -0,0 +1,113 @@
1
+ # Bevor CLI
2
+
3
+ The official command-line interface for [Bevor](https://bevor.io) — smart contract security analysis and code graph exploration.
4
+
5
+ Use `bevor` to upload Solidity codebases, run security analyses, explore the code graph interactively, and manage findings — all from your terminal.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ curl -LsSf https://bevor.io/install.sh | bash
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Setup
18
+
19
+ ```bash
20
+ bevor login
21
+ bevor link
22
+ #
23
+ bevor codes push
24
+ bevor analyses create
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Contents
30
+
31
+ `[]` -> optional args.
32
+ `<>` -> required args.
33
+
34
+ Simplification: EVERYTHING operates on the currently configured analysis. Findings as well.
35
+ Simplifies the DX, and we can take the stance that it's a rare state to want to operate on OTHER things via the CLI.
36
+ "version" is a flag that can be passed, but it's intentionally hard to act on arbitrary versions, unless the head is moved or the flag is passed.
37
+
38
+ ### Local Configuration
39
+ - workspace [id]
40
+ - project [id]
41
+ - code [id, fingerprint]
42
+ - analysis [id]
43
+
44
+ ```bash
45
+ help
46
+ login
47
+ logout
48
+ whoami [--json]
49
+ link
50
+ context [--json]
51
+ sessions
52
+ sessions list
53
+ sessions revoke <token_id>
54
+ workspaces
55
+ workspaces list [--json]
56
+ workspaces view [workspace_id] [--json]
57
+ workspaces select [workspace_id]
58
+ projects
59
+ projects list [--json]
60
+ projects view [project_id] [--json]
61
+ projects select [project_id]
62
+ projects create [--name] [--description] [--kind] [--yes]
63
+ codes
64
+ codes list [--page] [--page-size] [--order] [--order-by] [--project] [--network] [--identifier] [--status] [--json]
65
+ codes view [code_id] [--json]
66
+ codes select [code_id]
67
+ codes push [--path] [--no-wait] [--yes]
68
+ codes nodes
69
+ codes nodes list [--code] [--entrypoints] [--file] [--no-deps] [--name] [--node-type] [--json]
70
+ codes nodes view <node_id> [--code] [--json]
71
+ codes nodes content <node_id> [--code]
72
+ codes nodes edges <node_id> [--code] [--direction] [--type] [--json]
73
+ codes nodes call-chain <node_id> [--code] [--json]
74
+ codes nodes interactive <node_id> [--code]
75
+ analyses
76
+ analyses list [--page] [--page-size] [--order] [--order-by] [--project] [--user] [--is-owner] [--json]
77
+ analyses view [analysis_id] [--version] [--json]
78
+ analyses select [analysis_id] [--page] [--page-size]
79
+ analyses recent [--json]
80
+ analyses create [--name] [--collision] [--yes] [--json] [--force]
81
+ analyses set-head <version_id>
82
+ analyses run [--version] [--no-wait] [--silent]
83
+ analyses retry [--version] [--no-wait] [--silent]
84
+ analyses diff [--version] [--staged] [--json]
85
+ analyses commit [--version]
86
+ analyses child [--version] [--collision]
87
+ analyses fork [--version]
88
+ analyses merge <from_analysis> [--to]
89
+ analyses findings [--version] [--interactive] [--json] [--sarif]
90
+ analyses entrypoints [--version] [--json]
91
+ analyses transitions [--page] [--page-size] [--order] [--order-by] [--reason] [--version] [--project-wide] [--json]
92
+ analyses delete
93
+ analyses delete full
94
+ analyses delete version [version]
95
+ findings
96
+ findings list [--page] [--page-size] [--order] [--order-by] [--project] [--self] [--user] [--code] [--analyses] [--type] [--level] [--acknowledged] [--operation] [--created-by-self] [--created-by] [--json]
97
+ findings view <finding_id> [--json]
98
+ findings add [--version] [--file] [--interactive] [--type] [--level] [--name] [--explanation] [--recommendation] [--poc] [--tool] [--node] [--location] [--uri] [--start-line] [--start-column] [--end-line] [--end-column] [--char-offset] [--char-length] [--byte-offset] [--byte-length] [--fqn]
99
+ findings bulk <source> [--version]
100
+ findings edit <finding_id> [--file]
101
+ findings delete <finding_id>
102
+ findings revert <finding_id>
103
+ findings acknowledge <finding_id>
104
+ agent
105
+ agent setup
106
+ agent materialize
107
+ chats
108
+ chats list --page --page-size --order --order-by --project [--json]
109
+ chats view <chat_id> [--json]
110
+ chats messages <chat_id> [--json]
111
+ chats new <mode>
112
+ chats resume <chat_id>
113
+ ```
File without changes
File without changes
@@ -0,0 +1,16 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ YES = Annotated[bool, typer.Option("--yes", help="Skip confirmation of action.")]
6
+ NO_WAIT = Annotated[
7
+ bool,
8
+ typer.Option(
9
+ "--no-wait", help="Return immediately without waiting for the event stream to complete."
10
+ ),
11
+ ]
12
+ JSON_OUT = Annotated[bool, typer.Option("--json", help="JSON output")]
13
+ FORCE = Annotated[bool, typer.Option("--force", help="Skip checks. Not recommended.")]
14
+ SILENT = Annotated[bool, typer.Option("--silent", help="Suppress intermediate output logs.")]
15
+
16
+ __all__ = ["YES", "JSON_OUT", "NO_WAIT", "FORCE", "SILENT"]
@@ -0,0 +1,25 @@
1
+ from typing import Annotated, Literal
2
+
3
+ import typer
4
+
5
+ PAGE = Annotated[
6
+ int | None,
7
+ typer.Option("--page", help="Page number to use for resource that supports pagination"),
8
+ ]
9
+ PAGE_SIZE = Annotated[
10
+ int | None,
11
+ typer.Option("--page-size", help="Page size to use for resource that supports pagination"),
12
+ ]
13
+ ORDER = Annotated[
14
+ Literal["desc", "asc"] | None,
15
+ typer.Option("--order", help="How to order results when a resource supports pagination"),
16
+ ]
17
+
18
+ ORDER_BY = Annotated[
19
+ Literal["created_at", "updated_at"] | None,
20
+ typer.Option(
21
+ "--order-by", help="What field to order results by when a resource supports pagination"
22
+ ),
23
+ ]
24
+
25
+ __all__ = ["PAGE", "PAGE_SIZE", "ORDER", "ORDER_BY"]
@@ -0,0 +1,10 @@
1
+ import typer
2
+ from bevor_cli.commands.agent.materialze_cmd import scopes_materialize
3
+ from bevor_cli.commands.agent.setup_cmd import agent_setup
4
+
5
+ app = typer.Typer(help="Audit agent workflow shorthands.", no_args_is_help=True)
6
+
7
+ app.command("setup")(agent_setup)
8
+ app.command("materialize")(scopes_materialize)
9
+
10
+ __all__ = ["app"]
@@ -0,0 +1,47 @@
1
+ import typer
2
+ from bevor_cli.commands.analyses.active import active_analysis
3
+ from bevor_cli.commands.analyses.child import create_child_analysis
4
+ from bevor_cli.commands.analyses.commit import commit_analysis
5
+ from bevor_cli.commands.analyses.create import create_analysis
6
+ from bevor_cli.commands.analyses.delete import delete_analysis_full, delete_analysis_version
7
+ from bevor_cli.commands.analyses.diff import diff_analysis
8
+ from bevor_cli.commands.analyses.entrypoints import list_entrypoints
9
+ from bevor_cli.commands.analyses.findings import list_analysis_findings
10
+ from bevor_cli.commands.analyses.fork import fork_analysis
11
+ from bevor_cli.commands.analyses.list import list_analyses
12
+ from bevor_cli.commands.analyses.merge import merge_analysis
13
+ from bevor_cli.commands.analyses.retry import retry_analysis
14
+ from bevor_cli.commands.analyses.run import run_analysis
15
+ from bevor_cli.commands.analyses.select import select_analysis
16
+ from bevor_cli.commands.analyses.set_head import set_analysis_head
17
+ from bevor_cli.commands.analyses.transitions import list_transitions
18
+ from bevor_cli.commands.analyses.view import view_analysis
19
+
20
+ app = typer.Typer(help="Manage analyses.", no_args_is_help=True)
21
+
22
+ app.command("list")(list_analyses)
23
+ app.command("view")(view_analysis)
24
+ app.command("select")(select_analysis)
25
+ app.command("recent")(active_analysis)
26
+ app.command("create")(create_analysis)
27
+ app.command("set-head")(set_analysis_head)
28
+
29
+ delete_app = typer.Typer(help="Delete an analysis container or version.", no_args_is_help=True)
30
+ delete_app.command("full")(delete_analysis_full)
31
+ delete_app.command("version")(delete_analysis_version)
32
+ app.add_typer(delete_app, name="delete")
33
+
34
+ app.command("run")(run_analysis)
35
+ app.command("retry")(retry_analysis)
36
+ app.command("diff")(diff_analysis)
37
+ app.command("commit")(commit_analysis)
38
+ app.command("child")(create_child_analysis)
39
+ app.command("fork")(fork_analysis)
40
+ app.command("merge")(merge_analysis)
41
+ app.command("findings")(list_analysis_findings)
42
+
43
+
44
+ app.command("entrypoints")(list_entrypoints)
45
+ app.command("transitions")(list_transitions)
46
+
47
+ __all__ = ["app"]
@@ -0,0 +1,16 @@
1
+ import typer
2
+ from bevor_cli.commands.analyses.chats.get import get_chat
3
+ from bevor_cli.commands.analyses.chats.list import list_chats
4
+ from bevor_cli.commands.analyses.chats.messages import chat_messages
5
+ from bevor_cli.commands.analyses.chats.new import chat_new
6
+ from bevor_cli.commands.analyses.chats.resume import chat_resume
7
+
8
+ app = typer.Typer(help="Manage analysis chats.")
9
+
10
+ app.command("list")(list_chats)
11
+ app.command("view")(get_chat)
12
+ app.command("messages")(chat_messages)
13
+ app.command("new")(chat_new)
14
+ app.command("resume")(chat_resume)
15
+
16
+ __all__ = ["app"]
@@ -0,0 +1,18 @@
1
+ import typer
2
+ from bevor_cli.apps import nodes
3
+ from bevor_cli.commands.codes.list import list_code_versions
4
+ from bevor_cli.commands.codes.push import push_code
5
+ from bevor_cli.commands.codes.select import select_code
6
+ from bevor_cli.commands.codes.view import view_code
7
+
8
+ app = typer.Typer(
9
+ help="List, inspect, and upload code versions for the active project.",
10
+ no_args_is_help=True,
11
+ )
12
+ app.command("list")(list_code_versions)
13
+ app.command("view")(view_code)
14
+ app.command("select")(select_code)
15
+ app.command("push")(push_code)
16
+ app.add_typer(nodes.app, name="nodes")
17
+
18
+ __all__ = ["app"]
@@ -0,0 +1,22 @@
1
+ import typer
2
+ from bevor_cli.commands.findings.acknowledge import acknowledge_finding
3
+ from bevor_cli.commands.findings.add import add_finding
4
+ from bevor_cli.commands.findings.bulk import bulk_findings
5
+ from bevor_cli.commands.findings.delete import delete_finding
6
+ from bevor_cli.commands.findings.edit import edit_finding
7
+ from bevor_cli.commands.findings.get import get_finding
8
+ from bevor_cli.commands.findings.list import list_findings
9
+ from bevor_cli.commands.findings.revert import revert_finding
10
+
11
+ app = typer.Typer(help="Findings for the configured analysis.", no_args_is_help=True)
12
+
13
+ app.command("list")(list_findings)
14
+ app.command("view")(get_finding)
15
+ app.command("add")(add_finding)
16
+ app.command("bulk")(bulk_findings)
17
+ app.command("edit")(edit_finding)
18
+ app.command("delete")(delete_finding)
19
+ app.command("revert")(revert_finding)
20
+ app.command("acknowledge")(acknowledge_finding)
21
+
22
+ __all__ = ["app"]
@@ -0,0 +1,61 @@
1
+ import typer
2
+ from bevor_cli import output
3
+ from bevor_cli.apps import (
4
+ agent,
5
+ analyses,
6
+ chat,
7
+ codes,
8
+ findings,
9
+ projects,
10
+ sessions,
11
+ workspaces,
12
+ )
13
+ from bevor_cli.commands.auth import login, logout, whoami
14
+ from bevor_cli.commands.context import run_context
15
+ from bevor_cli.commands.link import run_link
16
+ from bevor_cli.output import console
17
+
18
+ app = typer.Typer(
19
+ name="bevor",
20
+ help="Bevor CLI — smart contract security analysis and code graph exploration.",
21
+ invoke_without_command=True,
22
+ no_args_is_help=True,
23
+ pretty_exceptions_enable=False,
24
+ )
25
+
26
+
27
+ @app.callback(invoke_without_command=True)
28
+ def _root(
29
+ quiet: bool = typer.Option(
30
+ False,
31
+ "--quiet",
32
+ help="Suppress informational output; useful for scripting.",
33
+ is_eager=False,
34
+ ),
35
+ ) -> None:
36
+ output.set_quiet(quiet)
37
+
38
+
39
+ @app.command("help")
40
+ def help_command(ctx: typer.Context) -> None:
41
+ """Show root help text (alias for --help)."""
42
+ root_ctx = ctx.parent or ctx
43
+ console.print(root_ctx.get_help())
44
+
45
+
46
+ app.command("login")(login)
47
+ app.command("logout")(logout)
48
+ app.command("whoami")(whoami)
49
+ app.command("link")(run_link)
50
+ app.command("context")(run_context)
51
+
52
+ app.add_typer(sessions.app, name="sessions")
53
+ app.add_typer(workspaces.app, name="workspaces")
54
+ app.add_typer(projects.app, name="projects")
55
+ app.add_typer(codes.app, name="codes")
56
+ app.add_typer(analyses.app, name="analyses")
57
+ app.add_typer(findings.app, name="findings")
58
+ app.add_typer(agent.app, name="agent")
59
+ app.add_typer(chat.app, name="chats")
60
+
61
+ __all__ = ["app"]
@@ -0,0 +1,17 @@
1
+ import typer
2
+ from bevor_cli.commands.graph.call_chain import call_chain_cmd
3
+ from bevor_cli.commands.graph.edges import list_node_edges
4
+ from bevor_cli.commands.graph.interactive import interactive_cmd
5
+ from bevor_cli.commands.nodes.get import get_node
6
+ from bevor_cli.commands.nodes.list import list_nodes
7
+ from bevor_cli.commands.nodes.source import show_source
8
+
9
+ app = typer.Typer(help="Inspect the code graph.", no_args_is_help=True)
10
+ app.command("list")(list_nodes)
11
+ app.command("view")(get_node)
12
+ app.command("content")(show_source)
13
+ app.command("edges")(list_node_edges)
14
+ app.command("call-chain")(call_chain_cmd)
15
+ app.command("interactive")(interactive_cmd)
16
+
17
+ __all__ = ["app"]
@@ -0,0 +1,14 @@
1
+ import typer
2
+ from bevor_cli.commands.projects.create import create_project
3
+ from bevor_cli.commands.projects.list import list_projects
4
+ from bevor_cli.commands.projects.select import select_project
5
+ from bevor_cli.commands.projects.view import view_project
6
+
7
+ app = typer.Typer(help="List, view, select, and create projects.", no_args_is_help=True)
8
+
9
+ app.command("list")(list_projects)
10
+ app.command("view")(view_project)
11
+ app.command("select")(select_project)
12
+ app.command("create")(create_project)
13
+
14
+ __all__ = ["app"]
@@ -0,0 +1,13 @@
1
+ import typer
2
+ from bevor_cli.commands.sessions.list import sessions_list
3
+ from bevor_cli.commands.sessions.revoke import sessions_revoke
4
+
5
+ app = typer.Typer(
6
+ help="List and revoke CLI sessions.",
7
+ no_args_is_help=True,
8
+ )
9
+
10
+ app.command("list")(sessions_list)
11
+ app.command("revoke")(sessions_revoke)
12
+
13
+ __all__ = ["app"]
@@ -0,0 +1,16 @@
1
+ import typer
2
+ from bevor_cli.commands.workspaces.list import list_workspaces
3
+ from bevor_cli.commands.workspaces.pick import pick_workspace
4
+ from bevor_cli.commands.workspaces.select import select_workspace
5
+ from bevor_cli.commands.workspaces.view import view_workspace
6
+
7
+ app = typer.Typer(
8
+ help="List, view, and select workspaces.",
9
+ no_args_is_help=True,
10
+ )
11
+
12
+ app.command("list")(list_workspaces)
13
+ app.command("view")(view_workspace)
14
+ app.command("select")(select_workspace)
15
+
16
+ __all__ = ["app", "pick_workspace"]
@@ -0,0 +1,157 @@
1
+ """Thin httpx wrapper that injects auth headers for the Bevor API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, cast
6
+
7
+ import httpx
8
+ import typer
9
+ from bevor_sdk import BevorClient
10
+ from bevor_sdk.exceptions import ERROR_REGISTRY, AppErrorTypes
11
+ from bevor_sdk.types.requests.manage import CreateProjectBodyDict
12
+ from bevor_sdk.types.responses import ResultsResponse
13
+ from bevor_sdk.types.responses.manage import (
14
+ ProjectSchema,
15
+ UserDetailedSchema,
16
+ WorkspaceDetailedSchema,
17
+ )
18
+ from httpx._types import QueryParamTypes, TimeoutTypes
19
+
20
+ from bevor_cli import output
21
+ from bevor_cli.configuration.creds import Credentials
22
+ from bevor_cli.settings import API_URL
23
+
24
+
25
+ class BevorInternalClient(BevorClient):
26
+ """Public SDK client authenticated with the CLI's user token.
27
+
28
+ Project-level endpoints resolve scope from the injected headers, so pass ``project_id`` for anything below
29
+ project selection.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ creds: Credentials,
35
+ workspace_id: str | None = None,
36
+ project_id: str | None = None,
37
+ ) -> None:
38
+ headers = {}
39
+
40
+ if not creds.token:
41
+ output.error("Not Authenticated", hint="Run `bevor login`.")
42
+ raise typer.Exit(1)
43
+
44
+ if workspace_id:
45
+ headers.update({"bevor-workspace-id": workspace_id})
46
+ if project_id:
47
+ headers.update({"bevor-project-id": project_id})
48
+ super().__init__(api_key=creds.token, base_url=API_URL, default_headers=headers)
49
+
50
+
51
+ class BevorInternalBusinessClient(httpx.Client):
52
+ """User-scoped endpoints that API keys can't reach: navigation above the project, sessions."""
53
+
54
+ _TIMEOUT = httpx.Timeout(10.0)
55
+
56
+ def __init__(
57
+ self, creds: Credentials, workspace_id: str | None = None, timeout: TimeoutTypes = None
58
+ ) -> None:
59
+ super().__init__(base_url=API_URL, timeout=timeout or self._TIMEOUT)
60
+
61
+ if creds.token:
62
+ self.headers.update({"Authorization": f"Bearer {creds.token}"})
63
+ if workspace_id:
64
+ self.headers.update({"Bevor-Workspace-Id": workspace_id})
65
+
66
+ def _parse_error_body(
67
+ self,
68
+ response: httpx.Response,
69
+ ) -> tuple[str | None, str, int]:
70
+ status = response.status_code
71
+ try:
72
+ body = response.json()
73
+ if not isinstance(body, dict):
74
+ return None, response.text or "no details", status
75
+ code_raw = body.get("code")
76
+ code: str | None
77
+ if code_raw is None:
78
+ code = None
79
+ elif isinstance(code_raw, str):
80
+ code = code_raw
81
+ else:
82
+ code = str(code_raw)
83
+ if body.get("message") is not None:
84
+ message = str(body.get("message"))
85
+ else:
86
+ d = body.get("detail")
87
+ if d is not None and not isinstance(d, str):
88
+ message = str(d)
89
+ else:
90
+ message = str(d) if d is not None else str(body)
91
+ return code, message, status
92
+ except Exception:
93
+ return None, response.text or "no details", status
94
+
95
+ def _handle_http_error(self, response: httpx.Response) -> bool:
96
+ if response.is_success:
97
+ if response.status_code == 204 or not response.content:
98
+ return False
99
+ return True
100
+
101
+ code, message, status = self._parse_error_body(response)
102
+
103
+ if not code:
104
+ # couldn't parse, so raise.
105
+ response.raise_for_status()
106
+
107
+ exc_cls = ERROR_REGISTRY.get(cast(AppErrorTypes, code))
108
+ if exc_cls is None:
109
+ response.raise_for_status()
110
+ return False # just for typing...
111
+
112
+ raise exc_cls(message)
113
+
114
+ def _prune_query_params(self, params: QueryParamTypes | None = None) -> QueryParamTypes | None:
115
+ if not params:
116
+ return None
117
+ qp = httpx.QueryParams(params)
118
+ out: Dict[str, Any] = {}
119
+ for k, v in qp.items():
120
+ if v is None:
121
+ continue
122
+ if v == "":
123
+ continue
124
+ if isinstance(v, bool):
125
+ out[k] = "true" if v else "false"
126
+ else:
127
+ out[k] = v
128
+ return httpx.QueryParams(out) if out else None
129
+
130
+ def _merge_queryparams(self, params: QueryParamTypes | None = None) -> QueryParamTypes | None:
131
+ params = super()._merge_queryparams(params)
132
+ return self._prune_query_params(params)
133
+
134
+ def send(self, request: httpx.Request, **kwargs) -> httpx.Response:
135
+ response = super().send(request, **kwargs)
136
+ self._handle_http_error(response)
137
+ return response
138
+
139
+ def me(self) -> UserDetailedSchema:
140
+ data = self.get("/v1/me").json()
141
+ return UserDetailedSchema.model_validate(data)
142
+
143
+ def list_workspaces(self) -> ResultsResponse[WorkspaceDetailedSchema]:
144
+ data = self.get("/v1/me/workspaces").json()
145
+ return ResultsResponse[WorkspaceDetailedSchema].model_validate(data)
146
+
147
+ def get_workspace(self) -> WorkspaceDetailedSchema:
148
+ data = self.get("/v1/workspace").json()
149
+ return WorkspaceDetailedSchema.model_validate(data)
150
+
151
+ def list_projects(self) -> ResultsResponse[ProjectSchema]:
152
+ data = self.get("/v1/workspace/projects").json()
153
+ return ResultsResponse[ProjectSchema].model_validate(data)
154
+
155
+ def create_project(self, body: CreateProjectBodyDict) -> ProjectSchema:
156
+ data = self.post("/v1/workspace/projects", json=body).json()
157
+ return ProjectSchema.model_validate(data)
File without changes
File without changes