ida-hcli 0.8.3.dev2__tar.gz → 0.8.3.dev4__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 (83) hide show
  1. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/PKG-INFO +1 -1
  2. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/pyproject.toml +1 -1
  3. ida_hcli-0.8.3.dev4/src/hcli/__init__.py +1 -0
  4. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/__init__.py +2 -2
  5. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/__init__.py +16 -0
  6. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/setup.py +221 -0
  7. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/source/__init__.py +18 -0
  8. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/source/add.py +35 -0
  9. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/source/list.py +31 -0
  10. ida_hcli-0.8.3.dev4/src/hcli/commands/ke/source/remove.py +31 -0
  11. ida_hcli-0.8.3.dev4/src/hcli/commands/open.py +86 -0
  12. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/PKG-INFO +1 -1
  13. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/SOURCES.txt +6 -0
  14. ida_hcli-0.8.3.dev2/src/hcli/__init__.py +0 -1
  15. ida_hcli-0.8.3.dev2/src/hcli/commands/open.py +0 -28
  16. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/LICENSE +0 -0
  17. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/README.md +0 -0
  18. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/setup.cfg +0 -0
  19. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/__init__.py +0 -0
  20. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/default.py +0 -0
  21. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/key/__init__.py +0 -0
  22. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/key/create.py +0 -0
  23. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/key/install.py +0 -0
  24. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/key/list.py +0 -0
  25. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/key/revoke.py +0 -0
  26. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/list.py +0 -0
  27. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/auth/switch.py +0 -0
  28. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/commands.py +0 -0
  29. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/common.py +0 -0
  30. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/download.py +0 -0
  31. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/extension/__init__.py +0 -0
  32. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/extension/create.py +0 -0
  33. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/extension/list.py +0 -0
  34. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/ida/__init__.py +0 -0
  35. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/ida/install.py +0 -0
  36. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/license/__init__.py +0 -0
  37. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/license/common.py +0 -0
  38. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/license/get.py +0 -0
  39. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/license/install.py +0 -0
  40. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/license/list.py +0 -0
  41. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/login.py +0 -0
  42. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/logout.py +0 -0
  43. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/share/__init__.py +0 -0
  44. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/share/delete.py +0 -0
  45. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/share/get.py +0 -0
  46. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/share/list.py +0 -0
  47. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/share/put.py +0 -0
  48. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/update.py +0 -0
  49. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/commands/whoami.py +0 -0
  50. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/env.py +0 -0
  51. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/__init__.py +0 -0
  52. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/__init__.py +0 -0
  53. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/asset.py +0 -0
  54. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/auth.py +0 -0
  55. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/common.py +0 -0
  56. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/customer.py +0 -0
  57. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/index.py +0 -0
  58. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/keys.py +0 -0
  59. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/api/license.py +0 -0
  60. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/auth/__init__.py +0 -0
  61. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/commands/__init__.py +0 -0
  62. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/config/__init__.py +0 -0
  63. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/console.py +0 -0
  64. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/constants/__init__.py +0 -0
  65. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/constants/auth.py +0 -0
  66. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/constants/cli.py +0 -0
  67. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/extensions/__init__.py +0 -0
  68. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/ida/__init__.py +0 -0
  69. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/ida/python.py +0 -0
  70. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/update/__init__.py +0 -0
  71. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/update/release.py +0 -0
  72. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/update/version.py +0 -0
  73. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/__init__.py +0 -0
  74. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/crc32.py +0 -0
  75. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/io.py +0 -0
  76. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/output.py +0 -0
  77. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/python.py +0 -0
  78. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/lib/util/string.py +0 -0
  79. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/hcli/main.py +0 -0
  80. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/dependency_links.txt +0 -0
  81. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/entry_points.txt +0 -0
  82. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/requires.txt +0 -0
  83. {ida_hcli-0.8.3.dev2 → ida_hcli-0.8.3.dev4}/src/ida_hcli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ida-hcli
3
- Version: 0.8.3.dev2
3
+ Version: 0.8.3.dev4
4
4
  Summary: HCLI - Hex-Rays CLI Utility
5
5
  Author-email: Hex-Rays SA <support@hex-rays.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ida-hcli"
3
- version = "0.8.3-dev.2"
3
+ version = "0.8.3-dev.4"
4
4
  description = "HCLI - Hex-Rays CLI Utility"
5
5
  requires-python = ">=3.10"
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "0.8.3-dev.4"
@@ -10,14 +10,12 @@ def register_commands(cli: click.Group) -> None:
10
10
  from .login import login
11
11
  from .logout import logout
12
12
  from .open import open_url
13
- from .setup import setup
14
13
  from .update import update
15
14
  from .whoami import whoami
16
15
  # placeholder for more commands
17
16
 
18
17
  cli.add_command(login)
19
18
  cli.add_command(logout)
20
- cli.add_command(setup)
21
19
  cli.add_command(whoami)
22
20
  cli.add_command(update)
23
21
  cli.add_command(download)
@@ -28,6 +26,7 @@ def register_commands(cli: click.Group) -> None:
28
26
  from .auth import auth
29
27
  from .extension import extension
30
28
  from .ida import ida
29
+ from .ke import ke
31
30
  from .license import license
32
31
  from .share import share
33
32
 
@@ -36,3 +35,4 @@ def register_commands(cli: click.Group) -> None:
36
35
  cli.add_command(share)
37
36
  cli.add_command(license)
38
37
  cli.add_command(extension)
38
+ cli.add_command(ke)
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ import rich_click as click
4
+
5
+
6
+ @click.group()
7
+ def ke() -> None:
8
+ """Knowledge Engine commands."""
9
+ pass
10
+
11
+
12
+ from .setup import setup # noqa: E402
13
+ from .source import source # noqa: E402
14
+
15
+ ke.add_command(source)
16
+ ke.add_command(setup)
@@ -0,0 +1,221 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import subprocess
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ import rich_click as click
11
+
12
+ from hcli.lib.commands import async_command
13
+ from hcli.lib.console import console
14
+ from hcli.lib.util.io import get_hcli_executable_path
15
+
16
+
17
+ def setup_macos_protocol_handler() -> None:
18
+ """Set up protocol handler for macOS using AppleScript and plist modification."""
19
+ try:
20
+ hcli_path = get_hcli_executable_path()
21
+
22
+ print(hcli_path)
23
+
24
+ # Create AppleScript application
25
+ applescript_content = f'''
26
+ on open location this_URL
27
+ do shell script "{hcli_path} open " & quoted form of this_URL
28
+ end open location
29
+
30
+ on run
31
+ -- This handler is called when the app is launched directly
32
+ end run
33
+ '''
34
+
35
+ # Create temporary directory for the AppleScript
36
+ with tempfile.TemporaryDirectory() as temp_dir:
37
+ script_path = Path(temp_dir) / "HCLIHandler.applescript"
38
+ app_path = Path.home() / "Applications" / "HCLIHandler.app"
39
+
40
+ # Write AppleScript
41
+ script_path.write_text(applescript_content)
42
+
43
+ # Compile AppleScript to application
44
+ subprocess.run(["osacompile", "-o", str(app_path), str(script_path)], check=True)
45
+
46
+ # Create Info.plist for the app to register URL scheme
47
+ info_plist_path = app_path / "Contents" / "Info.plist"
48
+
49
+ # Read existing plist
50
+ result = subprocess.run(
51
+ ["plutil", "-convert", "xml1", "-o", "-", str(info_plist_path)],
52
+ capture_output=True,
53
+ text=True,
54
+ check=True,
55
+ )
56
+
57
+ plist_content = result.stdout
58
+
59
+ # Add URL scheme handler to plist
60
+ url_scheme_xml = """
61
+ <key>CFBundleURLTypes</key>
62
+ <array>
63
+ <dict>
64
+ <key>CFBundleURLName</key>
65
+ <string>IDA URL Handler</string>
66
+ <key>CFBundleURLSchemes</key>
67
+ <array>
68
+ <string>ida</string>
69
+ </array>
70
+ </dict>
71
+ </array>"""
72
+
73
+ # Insert before closing </dict></plist>
74
+ if "<key>CFBundleURLTypes</key>" not in plist_content:
75
+ plist_content = plist_content.replace("</dict>\n</plist>", f"{url_scheme_xml}\n</dict>\n</plist>")
76
+
77
+ # Write back the modified plist
78
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".plist", delete=False) as temp_plist:
79
+ temp_plist.write(plist_content)
80
+ temp_plist_path = temp_plist.name
81
+
82
+ # Convert back to binary and replace original
83
+ subprocess.run(["plutil", "-convert", "binary1", temp_plist_path], check=True)
84
+
85
+ shutil.copy2(temp_plist_path, info_plist_path)
86
+ os.unlink(temp_plist_path)
87
+
88
+ # Register the app with Launch Services
89
+ subprocess.run(
90
+ [
91
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister",
92
+ "-f",
93
+ str(app_path),
94
+ ],
95
+ check=True,
96
+ )
97
+
98
+ console.print(f"[green]✓[/green] macOS protocol handler installed at {app_path}")
99
+
100
+ except subprocess.CalledProcessError as e:
101
+ console.print(f"[red]Failed to set up macOS protocol handler: {e}[/red]")
102
+ raise
103
+ except Exception as e:
104
+ console.print(f"[red]Error setting up macOS protocol handler: {e}[/red]")
105
+ raise
106
+
107
+
108
+ def setup_windows_protocol_handler() -> None:
109
+ """Set up protocol handler for Windows using registry entries."""
110
+ try:
111
+ import winreg # type: ignore[import-untyped]
112
+ from winreg import HKEY_CURRENT_USER, REG_SZ # type: ignore[import-untyped,attr-defined]
113
+
114
+ hcli_path = get_hcli_executable_path()
115
+ command = f'"{hcli_path}" open "%1"'
116
+
117
+ # Create registry entries for ida:// protocol
118
+ with winreg.CreateKey(HKEY_CURRENT_USER, r"SOFTWARE\Classes\ida") as key: # type: ignore[attr-defined]
119
+ winreg.SetValueEx(key, "", 0, REG_SZ, "URL:IDA Protocol") # type: ignore[attr-defined]
120
+ winreg.SetValueEx(key, "URL Protocol", 0, REG_SZ, "") # type: ignore[attr-defined]
121
+
122
+ with winreg.CreateKey(HKEY_CURRENT_USER, r"SOFTWARE\Classes\ida\DefaultIcon") as key: # type: ignore[attr-defined]
123
+ winreg.SetValueEx(key, "", 0, REG_SZ, f"{hcli_path},1") # type: ignore[attr-defined]
124
+
125
+ with winreg.CreateKey(HKEY_CURRENT_USER, r"SOFTWARE\Classes\ida\shell") as key: # type: ignore[attr-defined]
126
+ pass
127
+
128
+ with winreg.CreateKey(HKEY_CURRENT_USER, r"SOFTWARE\Classes\ida\shell\open") as key: # type: ignore[attr-defined]
129
+ pass
130
+
131
+ with winreg.CreateKey(HKEY_CURRENT_USER, r"SOFTWARE\Classes\ida\shell\open\command") as key: # type: ignore[attr-defined]
132
+ winreg.SetValueEx(key, "", 0, REG_SZ, command) # type: ignore[attr-defined]
133
+
134
+ console.print("[green]✓[/green] Windows protocol handler registered in registry")
135
+
136
+ except ImportError:
137
+ console.print("[red]winreg module not available (not on Windows?)[/red]")
138
+ raise
139
+ except Exception as e:
140
+ console.print(f"[red]Error setting up Windows protocol handler: {e}[/red]")
141
+ raise
142
+
143
+
144
+ def setup_linux_protocol_handler() -> None:
145
+ """Set up protocol handler for Linux using desktop entry and xdg-mime."""
146
+ try:
147
+ hcli_path = get_hcli_executable_path()
148
+
149
+ # Create desktop entry
150
+ desktop_entry_content = f"""[Desktop Entry]
151
+ Name=HCLI URL Handler
152
+ Exec={hcli_path} open %u
153
+ Type=Application
154
+ NoDisplay=true
155
+ MimeType=x-scheme-handler/ida;
156
+ """
157
+
158
+ # Write to applications directory
159
+ applications_dir = Path.home() / ".local" / "share" / "applications"
160
+ applications_dir.mkdir(parents=True, exist_ok=True)
161
+
162
+ desktop_file_path = applications_dir / "hcli-url-handler.desktop"
163
+ desktop_file_path.write_text(desktop_entry_content)
164
+
165
+ # Make executable
166
+ desktop_file_path.chmod(0o755)
167
+
168
+ # Register with xdg-mime
169
+ subprocess.run(["xdg-mime", "default", "hcli-url-handler.desktop", "x-scheme-handler/ida"], check=True)
170
+
171
+ # Update desktop database
172
+ subprocess.run(
173
+ ["update-desktop-database", str(applications_dir)], check=False
174
+ ) # May fail on some systems but not critical
175
+
176
+ console.print(f"[green]✓[/green] Linux protocol handler installed at {desktop_file_path}")
177
+
178
+ except subprocess.CalledProcessError as e:
179
+ console.print(f"[red]Failed to set up Linux protocol handler: {e}[/red]")
180
+ raise
181
+ except Exception as e:
182
+ console.print(f"[red]Error setting up Linux protocol handler: {e}[/red]")
183
+ raise
184
+
185
+
186
+ @click.command(name="setup")
187
+ @click.option("--force", is_flag=True, help="Force reinstall even if already configured")
188
+ @async_command
189
+ async def setup(force: bool = False) -> None:
190
+ """Set up hcli protocol handlers for ida:// URLs.
191
+
192
+ This command registers hcli as the handler for ida:// URLs on your system,
193
+ allowing web browsers and other applications to automatically open IDA-related
194
+ URLs with hcli.
195
+
196
+ The setup process varies by platform:
197
+ - macOS: Creates an AppleScript application and registers it with Launch Services
198
+ - Windows: Adds registry entries for the ida:// protocol
199
+ - Linux: Creates a desktop entry and registers with xdg-mime
200
+ """
201
+ current_platform = platform.system().lower()
202
+
203
+ console.print(f"[blue]Setting up hcli protocol handlers for {current_platform}...[/blue]")
204
+
205
+ try:
206
+ if current_platform == "darwin":
207
+ setup_macos_protocol_handler()
208
+ elif current_platform == "windows":
209
+ setup_windows_protocol_handler()
210
+ elif current_platform == "linux":
211
+ setup_linux_protocol_handler()
212
+ else:
213
+ console.print(f"[red]Unsupported platform: {current_platform}[/red]")
214
+ raise RuntimeError(f"Platform {current_platform} is not supported")
215
+
216
+ console.print("[green]✓ Protocol handler setup complete![/green]")
217
+ console.print("[yellow]You can now click ida:// links and they will open with hcli.[/yellow]")
218
+
219
+ except Exception as e:
220
+ console.print(f"[red]Setup failed: {e}[/red]")
221
+ raise
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import rich_click as click
4
+
5
+
6
+ @click.group()
7
+ def source() -> None:
8
+ """Manage knowledge sources."""
9
+ pass
10
+
11
+
12
+ from .add import add # noqa: E402
13
+ from .list import list_sources # noqa: E402
14
+ from .remove import remove # noqa: E402
15
+
16
+ source.add_command(add)
17
+ source.add_command(remove)
18
+ source.add_command(list_sources, name="list")
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import rich_click as click
6
+ from rich.console import Console
7
+
8
+ from hcli.lib.config import config_store
9
+
10
+ console = Console()
11
+
12
+
13
+ @click.command()
14
+ @click.argument("name", type=str)
15
+ @click.argument("path", type=click.Path(exists=True, path_type=Path))
16
+ def add(name: str, path: Path) -> None:
17
+ """Add a knowledge source.
18
+
19
+ NAME: Logical name for the source
20
+ PATH: Filesystem path to the source
21
+ """
22
+ # Get existing sources or initialize empty dict
23
+ sources: dict[str, str] = config_store.get_object("ke.sources", {}) or {}
24
+
25
+ if name in sources:
26
+ console.print(f"[yellow]Source '{name}' already exists. Use remove first to replace it.[/yellow]")
27
+ raise click.Abort()
28
+
29
+ # Store the absolute path as string
30
+ sources[name] = str(path.absolute())
31
+
32
+ # Save back to config
33
+ config_store.set_object("ke.sources", sources)
34
+
35
+ console.print(f"[green]Added source '{name}' pointing to '{path.absolute()}'[/green]")
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import rich_click as click
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+
7
+ from hcli.lib.config import config_store
8
+
9
+ console = Console()
10
+
11
+
12
+ @click.command()
13
+ def list_sources() -> None:
14
+ """List all knowledge sources."""
15
+ # Get existing sources
16
+ sources: dict[str, str] = config_store.get_object("ke.sources", {}) or {}
17
+
18
+ if not sources:
19
+ console.print("[yellow]No knowledge sources configured.[/yellow]")
20
+ return
21
+
22
+ # Create table
23
+ table = Table(show_header=True, header_style="bold magenta")
24
+ table.add_column("Name", style="cyan", width=20)
25
+ table.add_column("Path", style="white")
26
+
27
+ # Add rows
28
+ for name, path in sources.items():
29
+ table.add_row(name, path)
30
+
31
+ console.print(table)
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import rich_click as click
4
+ from rich.console import Console
5
+
6
+ from hcli.lib.config import config_store
7
+
8
+ console = Console()
9
+
10
+
11
+ @click.command()
12
+ @click.argument("name", type=str)
13
+ def remove(name: str) -> None:
14
+ """Remove a knowledge source.
15
+
16
+ NAME: Name of the source to remove
17
+ """
18
+ # Get existing sources
19
+ sources: dict[str, str] = config_store.get_object("ke.sources", {}) or {}
20
+
21
+ if name not in sources:
22
+ console.print(f"[red]Source '{name}' not found[/red]")
23
+ raise click.Abort()
24
+
25
+ # Remove the source
26
+ del sources[name]
27
+
28
+ # Save back to config
29
+ config_store.set_object("ke.sources", sources)
30
+
31
+ console.print(f"[green]Removed source '{name}'[/green]")
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from urllib.parse import urlparse
7
+
8
+ import rich_click as click
9
+ from rich.console import Console
10
+
11
+ from hcli.lib.commands import async_command
12
+ from hcli.lib.config import config_store
13
+ from hcli.lib.ida import IdaVersion, get_default_ida_install_directory, get_ida_binary_path
14
+
15
+ console = Console()
16
+
17
+
18
+ @click.command(name="open", hidden=True)
19
+ @click.argument("url", required=True)
20
+ @async_command
21
+ async def open_url(url: str | None) -> None:
22
+ """HCLI protocol handler for ida://"""
23
+
24
+ if not url:
25
+ console.print("[red]No URL provided[/red]")
26
+ raise click.Abort()
27
+
28
+ # Parse the URL
29
+ parsed_url = urlparse(url)
30
+
31
+ if parsed_url.scheme != "ida":
32
+ console.print(f"[red]Unsupported URL scheme: {parsed_url.scheme}[/red]")
33
+ raise click.Abort()
34
+
35
+ # Extract source name (hostname) and file path
36
+ source_name = parsed_url.netloc
37
+ file_path = parsed_url.path.lstrip("/") # Remove leading slash
38
+
39
+ if not source_name:
40
+ console.print("[red]No source name provided in URL[/red]")
41
+ raise click.Abort()
42
+
43
+ if not file_path:
44
+ console.print("[red]No file path provided in URL[/red]")
45
+ raise click.Abort()
46
+
47
+ # Get ke sources from config
48
+ sources: dict[str, str] = config_store.get_object("ke.sources", {}) or {}
49
+
50
+ if source_name not in sources:
51
+ console.print(f"[red]Source '{source_name}' not found. Available sources:[/red]")
52
+ if sources:
53
+ for name in sources.keys():
54
+ console.print(f" - {name}")
55
+ else:
56
+ console.print(" No sources configured. Use 'hcli ke source add' to add sources.")
57
+ raise click.Abort()
58
+
59
+ # Resolve full path
60
+ source_path = Path(sources[source_name])
61
+ full_path = source_path / file_path
62
+
63
+ if not full_path.exists():
64
+ console.print(f"[red]File not found: {full_path}[/red]")
65
+ raise click.Abort()
66
+
67
+ # Get IDA binary
68
+ try:
69
+ ida_dir = get_default_ida_install_directory(IdaVersion("IDA Professional", 9, 2))
70
+ ida_bin = get_ida_binary_path(ida_dir)
71
+ except Exception as e:
72
+ console.print(f"[red]IDA Pro not found: {e}[/red]")
73
+ console.print(f"[yellow]URL resolution successful: {url} -> {full_path}[/yellow]")
74
+ raise click.Abort()
75
+
76
+ # Log the URL to a temp file
77
+ log_file = "/tmp/hcli_urls.log"
78
+ timestamp = datetime.now().isoformat()
79
+
80
+ with open(str(log_file), "a", encoding="utf-8") as f:
81
+ f.write(f"{timestamp}: {url} -> {full_path} : {ida_bin}\n")
82
+
83
+ console.print(f"[green]Opening {full_path} with IDA Pro[/green]")
84
+
85
+ # Launch IDA with the resolved file path
86
+ subprocess.Popen([ida_bin, str(full_path)])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ida-hcli
3
- Version: 0.8.3.dev2
3
+ Version: 0.8.3.dev4
4
4
  Summary: HCLI - Hex-Rays CLI Utility
5
5
  Author-email: Hex-Rays SA <support@hex-rays.com>
6
6
  License-Expression: MIT
@@ -27,6 +27,12 @@ src/hcli/commands/extension/create.py
27
27
  src/hcli/commands/extension/list.py
28
28
  src/hcli/commands/ida/__init__.py
29
29
  src/hcli/commands/ida/install.py
30
+ src/hcli/commands/ke/__init__.py
31
+ src/hcli/commands/ke/setup.py
32
+ src/hcli/commands/ke/source/__init__.py
33
+ src/hcli/commands/ke/source/add.py
34
+ src/hcli/commands/ke/source/list.py
35
+ src/hcli/commands/ke/source/remove.py
30
36
  src/hcli/commands/license/__init__.py
31
37
  src/hcli/commands/license/common.py
32
38
  src/hcli/commands/license/get.py
@@ -1 +0,0 @@
1
- __version__ = "0.8.3-dev.2"
@@ -1,28 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import subprocess
4
- from datetime import datetime
5
-
6
- import rich_click as click
7
-
8
- from hcli.lib.commands import async_command
9
- from hcli.lib.ida import IdaVersion, get_default_ida_install_directory, get_ida_binary_path
10
-
11
-
12
- @click.command(name="open", hidden=True)
13
- @click.argument("url", required=True)
14
- @async_command
15
- async def open_url(url: str | None) -> None:
16
- """HCLI protocol handler for ida://"""
17
-
18
- ida_dir = get_default_ida_install_directory(IdaVersion("IDA Professional", 9, 2))
19
- ida_bin = get_ida_binary_path(ida_dir)
20
-
21
- # Log the URL to a temp file
22
- log_file = "/tmp/hcli_urls.log"
23
- timestamp = datetime.now().isoformat()
24
-
25
- with open(str(log_file), "a", encoding="utf-8") as f:
26
- f.write(f"{timestamp}: {url} : {ida_bin}\n")
27
-
28
- subprocess.Popen(["open", "-a", ida_bin])
File without changes
File without changes
File without changes