ntermqt 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl

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.
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script to verify new API backend works with REPL.
4
+
5
+ Run from nterm project directory:
6
+ python -m nterm.scripting.test_api_repl
7
+
8
+ Or in IPython:
9
+ %run nterm/scripting/test_api_repl.py
10
+ """
11
+
12
+ import sys
13
+ from pathlib import Path
14
+
15
+
16
+ def test_api_imports():
17
+ """Test that all new API components import correctly."""
18
+ print("=" * 60)
19
+ print("Testing API Imports")
20
+ print("=" * 60)
21
+
22
+ try:
23
+ from nterm.scripting.api import NTermAPI, get_api
24
+ print("✓ NTermAPI imported")
25
+ except ImportError as e:
26
+ print(f"✗ NTermAPI import failed: {e}")
27
+ return False
28
+
29
+ try:
30
+ from nterm.scripting.models import ActiveSession, CommandResult, DeviceInfo, CredentialInfo
31
+ print("✓ Models imported")
32
+ except ImportError as e:
33
+ print(f"✗ Models import failed: {e}")
34
+ return False
35
+
36
+ try:
37
+ from nterm.scripting.platform_data import PLATFORM_COMMANDS, PLATFORM_PATTERNS
38
+ print("✓ Platform data imported")
39
+ print(f" - {len(PLATFORM_PATTERNS)} platforms defined")
40
+ print(f" - {len(list(PLATFORM_COMMANDS.get('cisco_ios', {}).keys()))} command types for cisco_ios")
41
+ except ImportError as e:
42
+ print(f"✗ Platform data import failed: {e}")
43
+ return False
44
+
45
+ try:
46
+ from nterm.scripting.platform_utils import (
47
+ detect_platform,
48
+ get_platform_command,
49
+ extract_version_info,
50
+ extract_field,
51
+ )
52
+ print("✓ Platform utils imported")
53
+ except ImportError as e:
54
+ print(f"✗ Platform utils import failed: {e}")
55
+ return False
56
+
57
+ try:
58
+ from nterm.scripting.ssh_connection import (
59
+ connect_ssh,
60
+ send_command,
61
+ wait_for_prompt,
62
+ filter_ansi_sequences,
63
+ PagingNotDisabledError,
64
+ )
65
+ print("✓ SSH connection imported")
66
+ except ImportError as e:
67
+ print(f"✗ SSH connection import failed: {e}")
68
+ return False
69
+
70
+ print()
71
+ return True
72
+
73
+
74
+ def test_platform_utils():
75
+ """Test platform utility functions."""
76
+ print("=" * 60)
77
+ print("Testing Platform Utils")
78
+ print("=" * 60)
79
+
80
+ from nterm.scripting.platform_utils import (
81
+ detect_platform,
82
+ get_platform_command,
83
+ extract_field,
84
+ extract_version_info,
85
+ )
86
+
87
+ # Test detect_platform
88
+ test_cases = [
89
+ ("Cisco IOS Software, IOSv Software", "cisco_ios"),
90
+ ("Arista vEOS", "arista_eos"),
91
+ ("Cisco Nexus Operating System", "cisco_nxos"),
92
+ ("JUNOS 20.4R1", "juniper_junos"),
93
+ ("Unknown device", None),
94
+ ]
95
+
96
+ print("\ndetect_platform():")
97
+ for version_output, expected in test_cases:
98
+ result = detect_platform(version_output)
99
+ status = "✓" if result == expected else "✗"
100
+ print(f" {status} '{version_output[:30]}...' -> {result} (expected: {expected})")
101
+
102
+ # Test get_platform_command
103
+ print("\nget_platform_command():")
104
+
105
+ cmd = get_platform_command('cisco_ios', 'config')
106
+ print(f" cisco_ios + config -> '{cmd}'")
107
+
108
+ cmd = get_platform_command('juniper_junos', 'config')
109
+ print(f" juniper_junos + config -> '{cmd}'")
110
+
111
+ cmd = get_platform_command('cisco_ios', 'interface_detail', name='Gi0/1')
112
+ print(f" cisco_ios + interface_detail(name='Gi0/1') -> '{cmd}'")
113
+
114
+ cmd = get_platform_command('arista_eos', 'neighbors')
115
+ print(f" arista_eos + neighbors -> '{cmd}'")
116
+
117
+ # Test extract_field
118
+ print("\nextract_field():")
119
+ test_data = {'VERSION': '15.2(4)M', 'HARDWARE': ['ISR4451', 'ISR4451-X']}
120
+
121
+ result = extract_field(test_data, ['version', 'VERSION', 'SOFTWARE'])
122
+ print(f" Extract version from {test_data}: '{result}'")
123
+
124
+ result = extract_field(test_data, ['HARDWARE', 'MODEL'], first_element=True)
125
+ print(f" Extract hardware (first): '{result}'")
126
+
127
+ result = extract_field(test_data, ['HARDWARE', 'MODEL'], first_element=False)
128
+ print(f" Extract hardware (list): '{result}'")
129
+
130
+ # Test extract_version_info
131
+ print("\nextract_version_info():")
132
+ parsed_data = [{'VERSION': '15.2', 'HARDWARE': 'C3850', 'SERIAL': 'FCW1234'}]
133
+ info = extract_version_info(parsed_data, 'cisco_ios')
134
+ print(f" Extracted: {info}")
135
+
136
+ print()
137
+ return True
138
+
139
+
140
+ def test_ansi_filtering():
141
+ """Test ANSI sequence filtering."""
142
+ print("=" * 60)
143
+ print("Testing ANSI Filtering")
144
+ print("=" * 60)
145
+
146
+ from nterm.scripting.ssh_connection import filter_ansi_sequences
147
+
148
+ test_cases = [
149
+ # (input, expected_output_contains)
150
+ ("\x1b[24;1Hdevice#", "device#"),
151
+ ("\x1b[0m\x1b[2Kshow version\x1b[0m", "show version"),
152
+ ("\x1b[?25hrouter>", "router>"),
153
+ ("clean text", "clean text"),
154
+ ("\x07bell\x07", "bell"),
155
+ ]
156
+
157
+ for raw, expected_contains in test_cases:
158
+ filtered = filter_ansi_sequences(raw)
159
+ status = "✓" if expected_contains in filtered else "✗"
160
+ print(f" {status} Input: {repr(raw)[:40]}")
161
+ print(f" Output: '{filtered}'")
162
+
163
+ print()
164
+ return True
165
+
166
+
167
+ def test_api_initialization():
168
+ """Test API can be initialized."""
169
+ print("=" * 60)
170
+ print("Testing API Initialization")
171
+ print("=" * 60)
172
+
173
+ try:
174
+ from nterm.scripting.api import NTermAPI
175
+
176
+ # This will fail if tfsm_templates.db doesn't exist
177
+ # That's expected in some environments
178
+ api = NTermAPI()
179
+ print(f"✓ API initialized: {api}")
180
+
181
+ # Test status
182
+ status = api.status()
183
+ print(f" Devices: {status['devices']}")
184
+ print(f" Folders: {status['folders']}")
185
+ print(f" Vault: {'unlocked' if status['vault_unlocked'] else 'locked'}")
186
+ print(f" Parser DB: {status['parser_db']}")
187
+
188
+ # Test new methods exist
189
+ print("\nNew API methods:")
190
+ print(f" ✓ session() context manager: {hasattr(api, 'session')}")
191
+ print(f" ✓ send_first(): {hasattr(api, 'send_first')}")
192
+ print(f" ✓ send_platform_command(): {hasattr(api, 'send_platform_command')}")
193
+ print(f" ✓ disconnect_all(): {hasattr(api, 'disconnect_all')}")
194
+
195
+ # Test active_sessions returns correct type
196
+ sessions = api.active_sessions()
197
+ print(f" ✓ active_sessions() returns list: {isinstance(sessions, list)}")
198
+
199
+ print()
200
+ return True
201
+
202
+ except Exception as e:
203
+ print(f"✗ API initialization failed: {e}")
204
+ print(" (This is expected if tfsm_templates.db is not present)")
205
+ print()
206
+ return False
207
+
208
+
209
+ def test_repl_imports():
210
+ """Test REPL imports."""
211
+ print("=" * 60)
212
+ print("Testing REPL Imports")
213
+ print("=" * 60)
214
+
215
+ try:
216
+ from nterm.scripting.repl import NTermREPL, REPLPolicy, REPLState
217
+ print("✓ REPL classes imported")
218
+ except ImportError as e:
219
+ print(f"✗ REPL import failed: {e}")
220
+ return False
221
+
222
+ try:
223
+ from nterm.scripting.repl_interactive import start_repl
224
+ print("✓ Interactive REPL imported")
225
+ except ImportError as e:
226
+ print(f"✗ Interactive REPL import failed: {e}")
227
+ return False
228
+
229
+ # Test policy
230
+ policy = REPLPolicy(mode="read_only")
231
+ print(f"\nPolicy tests (mode={policy.mode}):")
232
+
233
+ test_commands = [
234
+ ("show version", True),
235
+ ("show interfaces", True),
236
+ ("configure terminal", False),
237
+ ("write memory", False),
238
+ ("reload", False),
239
+ ("copy running-config startup-config", False),
240
+ ]
241
+
242
+ for cmd, expected in test_commands:
243
+ allowed = policy.is_allowed(cmd)
244
+ status = "✓" if allowed == expected else "✗"
245
+ print(
246
+ f" {status} '{cmd}' -> {'allowed' if allowed else 'blocked'} (expected: {'allowed' if expected else 'blocked'})")
247
+
248
+ print()
249
+ return True
250
+
251
+
252
+ def run_all_tests():
253
+ """Run all tests."""
254
+ print("\n" + "=" * 60)
255
+ print("nterm API Backend Tests")
256
+ print("=" * 60 + "\n")
257
+
258
+ results = []
259
+
260
+ results.append(("Imports", test_api_imports()))
261
+ results.append(("Platform Utils", test_platform_utils()))
262
+ results.append(("ANSI Filtering", test_ansi_filtering()))
263
+ results.append(("API Init", test_api_initialization()))
264
+ results.append(("REPL", test_repl_imports()))
265
+
266
+ print("=" * 60)
267
+ print("Test Summary")
268
+ print("=" * 60)
269
+
270
+ passed = 0
271
+ failed = 0
272
+ for name, result in results:
273
+ status = "✓ PASS" if result else "✗ FAIL"
274
+ print(f" {status}: {name}")
275
+ if result:
276
+ passed += 1
277
+ else:
278
+ failed += 1
279
+
280
+ print()
281
+ print(f"Passed: {passed}/{len(results)}")
282
+ print(f"Failed: {failed}/{len(results)}")
283
+ print()
284
+
285
+ return failed == 0
286
+
287
+
288
+ if __name__ == "__main__":
289
+ success = run_all_tests()
290
+ sys.exit(0 if success else 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ntermqt
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Modern SSH terminal widget for PyQt6 with credential vault and jump host support
5
5
  Author: Scott Peterman
6
6
  License: GPL-3.0
@@ -85,10 +85,11 @@ Built for managing hundreds of devices through bastion hosts with hardware secur
85
85
  - Cross-platform keychain: macOS Keychain, Windows Credential Locker, Linux Secret Service
86
86
  - Full PyQt6 management UI
87
87
 
88
- **Scripting API** *(Experimental)*
88
+ **Scripting API**
89
89
  - Query device inventory and credentials programmatically
90
90
  - Built-in IPython console with API pre-loaded
91
- - CLI for shell scripts and automation
91
+ - **Platform-aware commands** - one API, correct syntax everywhere
92
+ - **Interactive REPL** with quick commands and structured output
92
93
  - Foundation for MCP tools and agentic workflows
93
94
 
94
95
  ---
@@ -111,7 +112,7 @@ nterm includes a built-in development console accessible via **Dev → IPython**
111
112
 
112
113
  ![IPython Console](https://raw.githubusercontent.com/scottpeterman/nterm/main/screenshots/ipython.png)
113
114
 
114
- ![IPython Console](https://raw.githubusercontent.com/scottpeterman/nterm/main/screenshots/repl.png)
115
+ ![IPython Console](https://raw.githubusercontent.com/scottpeterman/nterm/main/screenshots/repl_dark3.png)
115
116
 
116
117
  The IPython console runs in the same Python environment as nterm, with the scripting API pre-loaded. Query your device inventory, inspect credentials, and prototype automation workflows without leaving the app.
117
118
 
@@ -192,9 +193,9 @@ python -m nterm
192
193
 
193
194
  ---
194
195
 
195
- ## Scripting API *(Experimental)*
196
+ ## Scripting API
196
197
 
197
- nterm includes a scripting API for programmatic access to your device inventory and credential vault. Use it from IPython, CLI, or Python scripts.
198
+ nterm includes a full scripting API for programmatic access to your device inventory, credential vault, and network devices. Use it from IPython, CLI, or Python scripts.
198
199
 
199
200
  ### IPython Console
200
201
 
@@ -204,49 +205,103 @@ Open **Dev → IPython → Open in Tab** to get an interactive console with the
204
205
  api.devices() # List all saved devices
205
206
  api.search("leaf") # Search by name/hostname
206
207
  api.devices("eng-*") # Glob pattern filter
208
+ api.folders() # List all folders
207
209
 
208
210
  api.unlock("vault-password") # Unlock credential vault
209
211
  api.credentials() # List credentials (metadata only)
210
212
 
213
+ # Connect and execute commands
214
+ with api.session("usa-leaf-1") as s:
215
+ result = api.send(s, "show version")
216
+ print(result.parsed_data)
217
+
211
218
  api.help() # Show all commands
212
219
  ```
213
220
 
214
- ### CLI
221
+ ### Interactive REPL
215
222
 
216
- ```bash
217
- nterm-cli devices # List all devices
218
- nterm-cli search leaf # Search devices
219
- nterm-cli device eng-leaf-1 # Device details
220
- nterm-cli credentials --unlock # List credentials
221
- nterm-cli --json devices # JSON output for scripting
223
+ Start the REPL for interactive device exploration with platform-aware quick commands:
224
+
225
+ ```python
226
+ api.repl()
227
+ ```
228
+
229
+ ```
230
+ nterm> :unlock
231
+ nterm> :connect usa-leaf-1
232
+
233
+ 📊 usa-leaf-1> :version
234
+ ──────────────────────────────────────────────────
235
+ Version: 15.2(4.0.55)E
236
+ Hardware: IOSv
237
+ Serial: 9J0PD0QB9W1
238
+ Uptime: 1 week, 4 days, 7 minutes
239
+ ──────────────────────────────────────────────────
240
+
241
+ 📊 usa-leaf-1> :neighbors
242
+ Local Interface Neighbor Remote Port
243
+ ----------------------------------------------------------------
244
+ Gi0/0 usa-spine-2.lab.local Ethernet1
245
+ Gi0/1 usa-spine-1.lab.local Ethernet1
246
+
247
+ 📊 usa-leaf-1> :interfaces
248
+ [Rich formatted interface table]
222
249
  ```
223
250
 
251
+ **Quick Commands:**
252
+ - `:version` - Structured version info
253
+ - `:config` - Running configuration
254
+ - `:interfaces` - Interface status
255
+ - `:neighbors` - CDP/LLDP neighbors (auto-detects)
256
+ - `:bgp` - BGP summary
257
+ - `:routes` - Routing table
258
+
224
259
  ### Python Scripts
225
260
 
226
261
  ```python
227
262
  from nterm.scripting import NTermAPI
228
263
 
229
264
  api = NTermAPI()
265
+ api.unlock("vault-password")
230
266
 
231
- # Query devices
267
+ # Context manager for automatic cleanup
232
268
  for device in api.devices("*spine*"):
233
- print(f"{device.name}: {device.hostname}")
234
-
235
- # Work with credentials
236
- api.unlock("vault-password")
237
- cred = api.credential("lab-admin")
238
- print(f"Username: {cred.username}")
269
+ with api.session(device.name) as s:
270
+ # Platform-aware commands - works on Cisco, Arista, Juniper
271
+ result = api.send_platform_command(s, 'version')
272
+ print(f"{device.name}: {result.parsed_data[0].get('VERSION')}")
273
+
274
+ # Try multiple commands until one works
275
+ with api.session("router1") as s:
276
+ result = api.send_first(s, [
277
+ "show cdp neighbors detail",
278
+ "show lldp neighbors detail",
279
+ ])
239
280
  ```
240
281
 
241
- ### Roadmap
282
+ ### CLI
283
+
284
+ ```bash
285
+ nterm-cli devices # List all devices
286
+ nterm-cli search leaf # Search devices
287
+ nterm-cli device eng-leaf-1 # Device details
288
+ nterm-cli credentials --unlock # List credentials
289
+ nterm-cli --json devices # JSON output for scripting
290
+ ```
242
291
 
243
- The scripting API is the foundation for:
292
+ ### Key Features
244
293
 
245
- - **Command execution** `api.connect()` and `api.send()` for programmatic device interaction
246
- - **Batch operations** — Fan out commands across device groups
247
- - **MCP tool integration** Expose nterm capabilities to AI agents
294
+ | Feature | Description |
295
+ |---------|-------------|
296
+ | **Context Manager** | `with api.session()` auto-disconnects |
297
+ | **Platform-Aware** | `send_platform_command()` picks correct syntax |
298
+ | **Fallback Commands** | `send_first()` tries alternatives |
299
+ | **Structured Output** | TextFSM parsing to List[Dict] |
300
+ | **ANSI Filtering** | Clean output, no escape sequences |
301
+ | **Paging Detection** | Raises error if paging not disabled |
248
302
 
249
- See [scripting/README.md](nterm/scripting/README.md) for full API documentation.
303
+ See [scripting/README_API_IPython.md](nterm/scripting/README_API_IPython.md) for full API documentation.
304
+ See [scripting/README_REPL.md](nterm/scripting/README_REPL.md) for REPL documentation.
250
305
 
251
306
  ---
252
307
 
@@ -436,9 +491,14 @@ nterm/
436
491
  │ ├── resolver.py # Pattern-based resolution
437
492
  │ └── manager_ui.py # PyQt6 credential manager
438
493
  ├── manager/ # Session tree, connection dialogs
439
- └── scripting/ # API, CLI, automation support
440
- ├── api.py # NTermAPI class
441
- └── cli.py # nterm-cli entry point
494
+ └── scripting/ # API, REPL, automation support
495
+ ├── api.py # NTermAPI class
496
+ ├── models.py # ActiveSession, CommandResult, DeviceInfo
497
+ ├── platform_data.py # Platform commands and patterns
498
+ ├── platform_utils.py # Platform detection, extraction helpers
499
+ ├── ssh_connection.py # Low-level SSH with ANSI filtering
500
+ ├── repl.py # NTermREPL command router
501
+ └── repl_interactive.py # Interactive REPL display
442
502
  ```
443
503
 
444
504
  ---
@@ -14,16 +14,21 @@ nterm/manager/models.py,sha256=cvC2HzCRadNG1EYsnZN4C9YS6uolHGcUGGZtt-wzGF4,12237
14
14
  nterm/manager/settings.py,sha256=r6MTw_9r1Wl2UX_ALpXIuPbDvJ0D91Y8wRKq6Bfr_3g,9210
15
15
  nterm/manager/tree.py,sha256=I78wSjkSuyM6903II-XNyPug9saMSODUNBCHCDrq4ls,22397
16
16
  nterm/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- nterm/parser/api_help_dialog.py,sha256=qcmgNKjge8xwVNZeKZFu47Zn0SxZjyzE7cv9h91XGxg,16165
17
+ nterm/parser/api_help_dialog.py,sha256=pz7AYCMZ8o0dr4NezOK7Rl7e7mJvcZUndnrTrJv17lo,23990
18
18
  nterm/parser/ntc_download_dialog.py,sha256=TGaMCxKBTIOhGNUoLEJLnD0uAnwYWdbHdb9PRZEY604,14151
19
19
  nterm/parser/tfsm_engine.py,sha256=6p4wrNa9tQRuCmWgsR4E3rZTprpLmii5PNjoGpCQBCw,7954
20
20
  nterm/parser/tfsm_fire.py,sha256=AHbN6p4HlgcYDjLWb67CF9YfMSTk-3aetMswmEZyRVc,9222
21
21
  nterm/parser/tfsm_fire_tester.py,sha256=h2CAqTS6ZNHMUr4di2DBRHAWbBGiUTliOvm5jVG4ltI,79146
22
22
  nterm/scripting/__init__.py,sha256=vxbODaXR0IPneja3BuDHmjsHzQg03tFWtHO4Rc6vCTk,1099
23
- nterm/scripting/api.py,sha256=9Gnxyscu3ZYNagYWGn_-5zX6O0-j8UJ_gM6PEbdHuEA,48540
23
+ nterm/scripting/api.py,sha256=nWuANeYyT8mI1OsP9g3Z2lZohrrCpAZVss6t9Ba5Avg,39011
24
24
  nterm/scripting/cli.py,sha256=W2DK4ZnuutaArye_to7CBchg0ogClURxVbGsMdnj1y0,9187
25
- nterm/scripting/repl.py,sha256=VebSJu6dML7Ef0J4wLL8szdPESvsS2G8T5moyuF7nnU,14335
26
- nterm/scripting/repl_interactive.py,sha256=adpwRsbSfALS_0bwZPFayKUCyQefgvnO5uZwIWqKNFY,14880
25
+ nterm/scripting/models.py,sha256=zX90xtFYz0fqIPc0G8mRaoiZ1aRLm0koiHIvklUBflg,6858
26
+ nterm/scripting/platform_data.py,sha256=uCWBDS1HqmOdYNPojNlKBWLYhLiME9eoE0LWL-w6dkQ,10200
27
+ nterm/scripting/platform_utils.py,sha256=_vb1tMstlD9pQ4blD2keAkVbs9lkbFlTBEavI5oMd3Q,18478
28
+ nterm/scripting/repl.py,sha256=URcgs-c-bEYsw2JNCewEonNy1oLsGpzsBcq5xpYWseM,29152
29
+ nterm/scripting/repl_interactive.py,sha256=yQ-XjyELBCE5t4Gk3hBnO8eZCbCXquddLBqcQIKPhY8,17870
30
+ nterm/scripting/ssh_connection.py,sha256=p9EGPE3hgbceDVh7UdGz44cSi79Vl7g9Q4fKd-2T314,19270
31
+ nterm/scripting/test_api_repl.py,sha256=deeA_epPnBxgDGYMKcyXEWQOOpIXQNjDZL01FmOpKvs,8762
27
32
  nterm/session/__init__.py,sha256=FkgHF1WPz78JBOWHSC7LLynG2NqoR6aanNTRlEzsO6I,1612
28
33
  nterm/session/askpass_ssh.py,sha256=U-frmLBIXwE2L5ZCEtai91G1dVRSWKLCtxn88t_PqGs,14083
29
34
  nterm/session/base.py,sha256=NNFt2uy-rTkwigrHcdnfREk_QZDxNe0CoP16C-7oIWs,2475
@@ -61,8 +66,8 @@ nterm/vault/manager_ui.py,sha256=qle-W40j6L_pOR0AaOCeyU8myizFTRkISNrloCn0H_Y,345
61
66
  nterm/vault/profile.py,sha256=qM9TJf68RKdjtxo-sJehO7wS4iTi2G26BKbmlmHLA5M,6246
62
67
  nterm/vault/resolver.py,sha256=GWB2YR9H1MH98RGQBKvitIsjWT_-wSMLuddZNz4wbns,7800
63
68
  nterm/vault/store.py,sha256=_0Lfe0WKjm3uSAtxgn9qAPlpBOLCuq9SVgzqsE_qaGQ,21199
64
- ntermqt-0.1.7.dist-info/METADATA,sha256=Q1Dz7d9n2AEzMW048T195_Zp062eYKSicxtqH1e1n9o,13624
65
- ntermqt-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
66
- ntermqt-0.1.7.dist-info/entry_points.txt,sha256=Gunr-_3w-aSpfqoMuGKM2PJSCRo9hZ7K1BksUtp1yd8,130
67
- ntermqt-0.1.7.dist-info/top_level.txt,sha256=bZdnNLTHNRNqo9jsOQGUWF7h5st0xW_thH0n2QOxWUo,6
68
- ntermqt-0.1.7.dist-info/RECORD,,
69
+ ntermqt-0.1.9.dist-info/METADATA,sha256=ICYH7EjefVJLe_Hp-prpK8GPQE2TTqEaoX7-x2M0cOo,16040
70
+ ntermqt-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
+ ntermqt-0.1.9.dist-info/entry_points.txt,sha256=Gunr-_3w-aSpfqoMuGKM2PJSCRo9hZ7K1BksUtp1yd8,130
72
+ ntermqt-0.1.9.dist-info/top_level.txt,sha256=bZdnNLTHNRNqo9jsOQGUWF7h5st0xW_thH0n2QOxWUo,6
73
+ ntermqt-0.1.9.dist-info/RECORD,,