notrack-cli-agent 0.0.0__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.
- notrack_cli/__init__.py +2 -0
- notrack_cli/client.py +196 -0
- notrack_cli/main.py +811 -0
- notrack_cli/memory.py +73 -0
- notrack_cli/parser.py +36 -0
- notrack_cli/tools.py +233 -0
- notrack_cli_agent-0.0.0.dist-info/METADATA +63 -0
- notrack_cli_agent-0.0.0.dist-info/RECORD +11 -0
- notrack_cli_agent-0.0.0.dist-info/WHEEL +5 -0
- notrack_cli_agent-0.0.0.dist-info/entry_points.txt +2 -0
- notrack_cli_agent-0.0.0.dist-info/top_level.txt +1 -0
notrack_cli/memory.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
HISTORY_DIR = os.path.expanduser("~/.notrack_cli")
|
|
6
|
+
SESSION_FILE = os.path.join(HISTORY_DIR, "session_history.json")
|
|
7
|
+
CHECKPOINT_FILE = os.path.join(HISTORY_DIR, "checkpoint.json")
|
|
8
|
+
|
|
9
|
+
class MemoryManager:
|
|
10
|
+
@staticmethod
|
|
11
|
+
def ensure_dir():
|
|
12
|
+
os.makedirs(HISTORY_DIR, exist_ok=True)
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def load_history():
|
|
16
|
+
MemoryManager.ensure_dir()
|
|
17
|
+
if os.path.exists(SESSION_FILE):
|
|
18
|
+
try:
|
|
19
|
+
with open(SESSION_FILE, "r", encoding="utf-8") as f:
|
|
20
|
+
return json.load(f)
|
|
21
|
+
except Exception:
|
|
22
|
+
return []
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def save_history(messages):
|
|
27
|
+
MemoryManager.ensure_dir()
|
|
28
|
+
try:
|
|
29
|
+
with open(SESSION_FILE, "w", encoding="utf-8") as f:
|
|
30
|
+
json.dump(messages[-50:], f, ensure_ascii=False, indent=2)
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def save_checkpoint(user_input, response_so_far, current_chat_id):
|
|
36
|
+
MemoryManager.ensure_dir()
|
|
37
|
+
data = {
|
|
38
|
+
"timestamp": time.time(),
|
|
39
|
+
"chat_id": current_chat_id,
|
|
40
|
+
"user_input": user_input,
|
|
41
|
+
"partial_response": response_so_far
|
|
42
|
+
}
|
|
43
|
+
try:
|
|
44
|
+
with open(CHECKPOINT_FILE, "w", encoding="utf-8") as f:
|
|
45
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def load_checkpoint():
|
|
51
|
+
if os.path.exists(CHECKPOINT_FILE):
|
|
52
|
+
try:
|
|
53
|
+
with open(CHECKPOINT_FILE, "r", encoding="utf-8") as f:
|
|
54
|
+
return json.load(f)
|
|
55
|
+
except Exception:
|
|
56
|
+
return None
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def clear_checkpoint():
|
|
61
|
+
if os.path.exists(CHECKPOINT_FILE):
|
|
62
|
+
try:
|
|
63
|
+
os.remove(CHECKPOINT_FILE)
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def clear_history():
|
|
69
|
+
if os.path.exists(SESSION_FILE):
|
|
70
|
+
try:
|
|
71
|
+
os.remove(SESSION_FILE)
|
|
72
|
+
except Exception:
|
|
73
|
+
pass
|
notrack_cli/parser.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Parse only explicit structured tool calls emitted by the model.
|
|
2
|
+
|
|
3
|
+
Plain prose or a fenced source-code sample is never interpreted as permission
|
|
4
|
+
for local execution.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
_TAG_RE = re.compile(
|
|
10
|
+
r'<tool_call:([a-zA-Z_]\w*)>\s*(.*?)\s*</tool_call:\1>',
|
|
11
|
+
re.DOTALL | re.IGNORECASE,
|
|
12
|
+
)
|
|
13
|
+
_FENCE_RE = re.compile(
|
|
14
|
+
r'```tool_call:([a-zA-Z_]\w*)\s*\n(.*?)\n```',
|
|
15
|
+
re.DOTALL | re.IGNORECASE,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def extract_tool_calls(text: str) -> list[tuple[str, dict, str]]:
|
|
20
|
+
"""Return explicit (tool name, JSON arguments, raw tag) calls only."""
|
|
21
|
+
calls = []
|
|
22
|
+
for pattern in (_TAG_RE, _FENCE_RE):
|
|
23
|
+
for match in pattern.finditer(text):
|
|
24
|
+
try:
|
|
25
|
+
args = json.loads(match.group(2))
|
|
26
|
+
except (json.JSONDecodeError, TypeError):
|
|
27
|
+
continue
|
|
28
|
+
if isinstance(args, dict):
|
|
29
|
+
calls.append((match.group(1), args, match.group(0)))
|
|
30
|
+
# Prevent duplicate calls if content somehow matches both grammars.
|
|
31
|
+
seen, unique = set(), []
|
|
32
|
+
for call in calls:
|
|
33
|
+
if call[2] not in seen:
|
|
34
|
+
seen.add(call[2])
|
|
35
|
+
unique.append(call)
|
|
36
|
+
return unique
|
notrack_cli/tools.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import shutil
|
|
4
|
+
import urllib.request
|
|
5
|
+
import json
|
|
6
|
+
import platform
|
|
7
|
+
|
|
8
|
+
# ──────────────────────────────────────────────────────────────
|
|
9
|
+
# SYSTEM PROMPT (sent to NoTrack AI on first turn)
|
|
10
|
+
# ──────────────────────────────────────────────────────────────
|
|
11
|
+
SYSTEM_PROMPT = """You are NoTrack Agent — an elite autonomous AI engineer and pair-programmer technically and physically created by Chris(Chr15zKH4x), running directly inside the user's terminal (Termux, Linux, macOS).
|
|
12
|
+
|
|
13
|
+
━━━ IDENTITY ━━━
|
|
14
|
+
You are a senior software architect + DevOps engineer + security researcher in one.
|
|
15
|
+
You produce COMPLETE, PRODUCTION-GRADE, WORKING code. Never write stubs or placeholders.
|
|
16
|
+
You are direct and technically precise. No filler phrases. No apologies.
|
|
17
|
+
When given a coding task: PLAN → ACT (use tools) → VERIFY (run tests/commands).
|
|
18
|
+
|
|
19
|
+
━━━ CRITICAL RULE ━━━
|
|
20
|
+
You have real tools that execute on the user's system RIGHT NOW.
|
|
21
|
+
When the user asks you to "create a file", "write a script", "run a command":
|
|
22
|
+
→ USE THE TOOL. Do NOT just show code in markdown. USE write_file or run_command.
|
|
23
|
+
→ The CLI will execute your tool calls and show the results live.
|
|
24
|
+
|
|
25
|
+
━━━ AVAILABLE TOOLS ━━━
|
|
26
|
+
|
|
27
|
+
1. CREATE OR WRITE A FILE (creates parent dirs automatically):
|
|
28
|
+
<tool_call:write_file>
|
|
29
|
+
{"path": "relative/or/absolute/path.ext", "content": "complete file content here"}
|
|
30
|
+
</tool_call:write_file>
|
|
31
|
+
|
|
32
|
+
2. READ A FILE:
|
|
33
|
+
<tool_call:view_file>
|
|
34
|
+
{"path": "path/to/file"}
|
|
35
|
+
</tool_call:view_file>
|
|
36
|
+
|
|
37
|
+
3. RUN ANY SHELL COMMAND (git, pip, npm, python, bash, rm, mkdir, zip, unzip — all allowed):
|
|
38
|
+
<tool_call:run_command>
|
|
39
|
+
{"command": "git push origin main"}
|
|
40
|
+
</tool_call:run_command>
|
|
41
|
+
|
|
42
|
+
4. LIST DIRECTORY:
|
|
43
|
+
<tool_call:list_dir>
|
|
44
|
+
{"path": "."}
|
|
45
|
+
</tool_call:list_dir>
|
|
46
|
+
|
|
47
|
+
5. SPAWN A PARALLEL SUB-AGENT for complex sub-tasks:
|
|
48
|
+
<tool_call:sub_agent>
|
|
49
|
+
{"task": "Detailed autonomous sub-task description"}
|
|
50
|
+
</tool_call:sub_agent>
|
|
51
|
+
|
|
52
|
+
6. FETCH A WEB PAGE / DOCUMENTATION:
|
|
53
|
+
<tool_call:fetch_url>
|
|
54
|
+
{"url": "https://chr15zkh4x.github.io"}
|
|
55
|
+
</tool_call:fetch_url>
|
|
56
|
+
|
|
57
|
+
7. SEARCH TEXT ACROSS FILES (Grep):
|
|
58
|
+
<tool_call:search_in_files>
|
|
59
|
+
{"query": "def my_function", "path": "."}
|
|
60
|
+
</tool_call:search_in_files>
|
|
61
|
+
|
|
62
|
+
8. GET SYSTEM INFO:
|
|
63
|
+
<tool_call:get_system_info>
|
|
64
|
+
{}
|
|
65
|
+
</tool_call:get_system_info>
|
|
66
|
+
|
|
67
|
+
━━━ BEHAVIORAL RULES ━━━
|
|
68
|
+
- Always use write_file to create files — never just show code in markdown.
|
|
69
|
+
- Always use run_command to execute things — never just say "run this".
|
|
70
|
+
- Chain tool calls: write → run → check output → fix if needed.
|
|
71
|
+
- For git: commit with meaningful messages, push after completing features.
|
|
72
|
+
- For Python: always check syntax with `python -m py_compile file.py` after writing.
|
|
73
|
+
- For projects: always create a proper directory structure, README, requirements.txt.
|
|
74
|
+
- If a command fails: read the error, fix it, run again. Keep going until it works.
|
|
75
|
+
- After finishing a multi-step task: summarize what was done.
|
|
76
|
+
|
|
77
|
+
━━━ CODE QUALITY STANDARDS ━━━
|
|
78
|
+
- Complete code only — no "add your code here" comments.
|
|
79
|
+
- Proper error handling (try/except, error codes).
|
|
80
|
+
- Type hints for Python.
|
|
81
|
+
- Environment variables for secrets — never hardcode.
|
|
82
|
+
- Tests if the user asks for them.
|
|
83
|
+
- Production-ready: logging, edge cases, input validation.
|
|
84
|
+
|
|
85
|
+
━━━ CONVERSATION STYLE ━━━
|
|
86
|
+
- Be concise and professional.
|
|
87
|
+
- When you need clarification: ask one focused question.
|
|
88
|
+
- When something is risky/destructive: say so clearly.
|
|
89
|
+
- Format your analysis/explanations with markdown headers.
|
|
90
|
+
- Show tool usage BEFORE the explanation (act first, explain after).
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class LocalTools:
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _resolve(path: str, cwd: str = None) -> str:
|
|
98
|
+
"""Resolve path relative to cwd if not absolute, and expand ~."""
|
|
99
|
+
expanded = os.path.expanduser(path)
|
|
100
|
+
if cwd and not os.path.isabs(expanded):
|
|
101
|
+
return os.path.realpath(os.path.join(cwd, expanded))
|
|
102
|
+
return os.path.realpath(expanded)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def view_file(path: str, cwd: str = None) -> str:
|
|
106
|
+
resolved = LocalTools._resolve(path, cwd)
|
|
107
|
+
try:
|
|
108
|
+
if not os.path.exists(resolved):
|
|
109
|
+
return f"Error: File not found: '{resolved}'"
|
|
110
|
+
with open(resolved, "r", encoding="utf-8", errors="ignore") as f:
|
|
111
|
+
content = f.read()
|
|
112
|
+
lines = content.splitlines()
|
|
113
|
+
if len(lines) > 1000:
|
|
114
|
+
content = "\n".join(lines[:1000]) + f"\n… [{len(lines)-1000} more lines]"
|
|
115
|
+
return content
|
|
116
|
+
except Exception as e:
|
|
117
|
+
return f"Error reading '{resolved}': {e}"
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def write_file(path: str, content: str, cwd: str = None) -> str:
|
|
121
|
+
resolved = LocalTools._resolve(path, cwd)
|
|
122
|
+
try:
|
|
123
|
+
os.makedirs(os.path.dirname(resolved), exist_ok=True)
|
|
124
|
+
with open(resolved, "w", encoding="utf-8") as f:
|
|
125
|
+
f.write(content)
|
|
126
|
+
return f"Written {len(content):,} bytes to '{resolved}'"
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return f"Error writing '{resolved}': {e}"
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def run_command(command: str, cwd: str = None, timeout: int = 120) -> str:
|
|
132
|
+
"""Execute any shell command with full privileges. No restrictions."""
|
|
133
|
+
work_dir = cwd or os.getcwd()
|
|
134
|
+
try:
|
|
135
|
+
result = subprocess.run(
|
|
136
|
+
command,
|
|
137
|
+
shell=True,
|
|
138
|
+
capture_output=True,
|
|
139
|
+
text=True,
|
|
140
|
+
timeout=timeout,
|
|
141
|
+
cwd=work_dir,
|
|
142
|
+
env={**os.environ, "TERM": "xterm-256color"},
|
|
143
|
+
)
|
|
144
|
+
parts = []
|
|
145
|
+
if result.stdout.strip():
|
|
146
|
+
parts.append(result.stdout.strip())
|
|
147
|
+
if result.stderr.strip():
|
|
148
|
+
parts.append(f"[STDERR]\n{result.stderr.strip()}")
|
|
149
|
+
if result.returncode != 0:
|
|
150
|
+
parts.append(f"[Exit {result.returncode}]")
|
|
151
|
+
return "\n\n".join(parts) if parts else "(command completed — no output)"
|
|
152
|
+
except subprocess.TimeoutExpired:
|
|
153
|
+
return f"Error: Command timed out after {timeout}s"
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return f"Error: {e}"
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def list_dir(path: str = ".", cwd: str = None) -> str:
|
|
159
|
+
resolved = LocalTools._resolve(path, cwd)
|
|
160
|
+
try:
|
|
161
|
+
items = sorted(os.listdir(resolved))
|
|
162
|
+
lines = []
|
|
163
|
+
for item in items:
|
|
164
|
+
full = os.path.join(resolved, item)
|
|
165
|
+
if os.path.isdir(full):
|
|
166
|
+
try:
|
|
167
|
+
n = len(os.listdir(full))
|
|
168
|
+
lines.append(f"📁 {item}/ ({n} items)")
|
|
169
|
+
except PermissionError:
|
|
170
|
+
lines.append(f"📁 {item}/ (permission denied)")
|
|
171
|
+
else:
|
|
172
|
+
size = os.path.getsize(full)
|
|
173
|
+
lines.append(f"📄 {item} [{size:,} B]")
|
|
174
|
+
return "\n".join(lines) if lines else "(empty directory)"
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return f"Error listing '{resolved}': {e}"
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def fetch_url(url: str) -> str:
|
|
180
|
+
"""Download text content from a URL (documentation, APIs, etc.)."""
|
|
181
|
+
try:
|
|
182
|
+
req = urllib.request.Request(url, headers={'User-Agent': 'NoTrack-CLI'})
|
|
183
|
+
with urllib.request.urlopen(req, timeout=15) as response:
|
|
184
|
+
content = response.read().decode('utf-8', errors='ignore')
|
|
185
|
+
if len(content) > 15000:
|
|
186
|
+
content = content[:15000] + "\n… [Content truncated due to length]"
|
|
187
|
+
return content
|
|
188
|
+
except Exception as e:
|
|
189
|
+
return f"Error fetching URL '{url}': {e}"
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def search_in_files(query: str, path: str = ".", extension: str = None, cwd: str = None) -> str:
|
|
193
|
+
"""Search for a text string inside files recursively."""
|
|
194
|
+
resolved = LocalTools._resolve(path, cwd)
|
|
195
|
+
results = []
|
|
196
|
+
try:
|
|
197
|
+
for root, dirs, files in os.walk(resolved):
|
|
198
|
+
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
199
|
+
for file in files:
|
|
200
|
+
if extension and not file.endswith(extension):
|
|
201
|
+
continue
|
|
202
|
+
full_path = os.path.join(root, file)
|
|
203
|
+
try:
|
|
204
|
+
with open(full_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
205
|
+
for line_no, line in enumerate(f, 1):
|
|
206
|
+
if query.lower() in line.lower():
|
|
207
|
+
rel_path = os.path.relpath(full_path, resolved)
|
|
208
|
+
results.append(f"{rel_path}:{line_no}: {line.strip()}")
|
|
209
|
+
if len(results) >= 40:
|
|
210
|
+
return "\n".join(results) + "\n… [Results truncated at 40 matches]"
|
|
211
|
+
except Exception:
|
|
212
|
+
continue
|
|
213
|
+
return "\n".join(results) if results else "No matches found."
|
|
214
|
+
except Exception as e:
|
|
215
|
+
return f"Error searching files: {e}"
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def get_system_info() -> str:
|
|
219
|
+
"""Return system specs, disk space, and OS details."""
|
|
220
|
+
try:
|
|
221
|
+
total, used, free = shutil.disk_usage("/")
|
|
222
|
+
info = {
|
|
223
|
+
"os": platform.system(),
|
|
224
|
+
"release": platform.release(),
|
|
225
|
+
"machine": platform.machine(),
|
|
226
|
+
"python_version": platform.python_version(),
|
|
227
|
+
"disk_total_gb": round(total / (2**30), 2),
|
|
228
|
+
"disk_free_gb": round(free / (2**30), 2),
|
|
229
|
+
"cpu_count": os.cpu_count(),
|
|
230
|
+
}
|
|
231
|
+
return json.dumps(info, indent=2)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
return f"Error getting system info: {e}"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: notrack-cli-agent
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Terminal agent client for NoTrack chat with supervised local tools
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests>=2.31
|
|
8
|
+
Requires-Dist: rich>=13.7
|
|
9
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
10
|
+
|
|
11
|
+
# NoTrack CLI Agent 0.6.0
|
|
12
|
+
|
|
13
|
+
Terminal chat client for NoTrack with a supervised local tool loop.
|
|
14
|
+
|
|
15
|
+
## Install (Linux / Termux / macOS)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m venv .venv
|
|
19
|
+
# Linux/macOS
|
|
20
|
+
. .venv/bin/activate
|
|
21
|
+
# Termux: activate the venv using the shell command above if supported
|
|
22
|
+
python -m pip install -U pip
|
|
23
|
+
python -m pip install -e .
|
|
24
|
+
notrack
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run a single prompt:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
notrack ask "Summarize the files in this folder"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
- `/new` starts a fresh chat context.
|
|
36
|
+
- `/chats` attempts to list conversations visible to this HTTP session.
|
|
37
|
+
- `/resume <chat_id>` selects a previously known chat identifier.
|
|
38
|
+
- `/tools`, `/history`, `/save`, `/pwd`, `/ls`, `/cd <path>`, `/help`, `/exit`.
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
- `NOTRACK_BASE_URL` defaults to `https://notrack.ai`
|
|
43
|
+
- `NOTRACK_DISPATCH_PATH` defaults to `/api/dispatch`
|
|
44
|
+
- `NOTRACK_MODEL` defaults to `notrack`
|
|
45
|
+
- `NOTRACK_MODE` defaults to `usual`
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
NOTRACK_DISPATCH_PATH=/api/chat notrack
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Important protocol note
|
|
54
|
+
|
|
55
|
+
The app's dispatch POST payload is an informed compatibility attempt based on the original project and observed endpoint behavior; NoTrack's current web protocol has not been live-verified in this package. The web site may rely on session cookies, CSRF state, headers, or a changing payload. A successful GET of `/api/chats` does not guarantee that a fresh Python session sees the same anonymous account as Firefox. This client does not copy Firefox credentials or automate Firefox.
|
|
56
|
+
|
|
57
|
+
## Safety
|
|
58
|
+
|
|
59
|
+
Local shell and file operations require confirmation by default. Read `RULE.md` before enabling any unattended mode. Treat all agent-generated commands as untrusted until reviewed.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## v0.6.2 protocol adjustment
|
|
63
|
+
This build uses the observed `normal` persona, includes `regenerate: false`, and creates anonymous web-style cookies per process. This is based on a public third-party integration example, not a guaranteed official web API contract. Each CLI process creates a fresh anonymous identity; it does not restore Firefox's identity or chats automatically.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
notrack_cli/__init__.py,sha256=BvQx3CzfQRKK4VLyev4xQnVCeGtri1uvTCj7qRbVWbY,49
|
|
2
|
+
notrack_cli/client.py,sha256=6GBi797I60iNY6lItI1pYpbl_qGN44HBPExDCBafN1M,8576
|
|
3
|
+
notrack_cli/main.py,sha256=9KEFC6mxYxbApbi_Aq4ZOZqA9eOTnOHgHmppY7_ujyg,35380
|
|
4
|
+
notrack_cli/memory.py,sha256=KHaJ99m5ypx0y6RLXWr2hVfTE38u_QClV0PmsSazcEQ,2122
|
|
5
|
+
notrack_cli/parser.py,sha256=I2eDpPAneDwfFmr_SIBzZHs4jlyVNzwa_OHVAy26iEU,1152
|
|
6
|
+
notrack_cli/tools.py,sha256=uTTRRkuTHNa4mIZXhx4d1SgtHjjg_f9t0-V6CGybzMI,9984
|
|
7
|
+
notrack_cli_agent-0.0.0.dist-info/METADATA,sha256=KAUuEthZD9bQe2HIdu52xaz73LhZg5T5a2dS6Ae7IOg,2336
|
|
8
|
+
notrack_cli_agent-0.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
9
|
+
notrack_cli_agent-0.0.0.dist-info/entry_points.txt,sha256=ZLK2VawIjc2kOd6OCEA9_S8z1I2GJGgVH5i0-9zh6To,50
|
|
10
|
+
notrack_cli_agent-0.0.0.dist-info/top_level.txt,sha256=3IINcBiTUEdKBE1_Oi1cpPopqmjc5jkfehxZwkLmEwg,12
|
|
11
|
+
notrack_cli_agent-0.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
notrack_cli
|