localcode 0.3.4__py3-none-any.whl → 0.3.5__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.
- localcode/__init__.py +1 -1
- localcode/agent/loop.py +12 -12
- localcode/agent/prompts.py +6 -3
- localcode/memory_guard.py +58 -0
- localcode/server_manager.py +23 -23
- localcode/tools/write_file.py +13 -0
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/METADATA +1 -1
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/RECORD +12 -12
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/WHEEL +0 -0
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/entry_points.txt +0 -0
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/licenses/LICENSE +0 -0
- {localcode-0.3.4.dist-info → localcode-0.3.5.dist-info}/top_level.txt +0 -0
localcode/__init__.py
CHANGED
localcode/agent/loop.py
CHANGED
|
@@ -715,22 +715,22 @@ def run_agent_loop(
|
|
|
715
715
|
list(getattr(_tool_exec_state, "files_read", {}) or {}),
|
|
716
716
|
progress_ledger_budget_chars(_win_tokens),
|
|
717
717
|
)
|
|
718
|
-
if _ledger:
|
|
719
|
-
model_messages = list(model_messages) + [
|
|
720
|
-
{"role": "user", "content": _ledger}
|
|
721
|
-
]
|
|
722
718
|
except Exception:
|
|
723
|
-
|
|
724
|
-
#
|
|
725
|
-
|
|
726
|
-
# advances instead of repeating. Appended LAST, ephemeral, empty-safe.
|
|
719
|
+
_ledger = ""
|
|
720
|
+
# Working-memory checklist (todo_write): model sees its own plan.
|
|
721
|
+
_todo_note = ""
|
|
727
722
|
try:
|
|
728
723
|
from ..tools.todo_write import render_todo_reminder
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
724
|
+
_todo_note = render_todo_reminder(list(getattr(app.session, "todos", []) or []))
|
|
725
|
+
except Exception:
|
|
726
|
+
_todo_note = ""
|
|
727
|
+
# Inject ledger + todo as ONE trailing SYSTEM message (not role:user
|
|
728
|
+
# — as a user turn the model re-greeted every round). Ephemeral.
|
|
729
|
+
try:
|
|
730
|
+
_ctx_block = "\n\n".join(b for b in (_ledger, _todo_note) if b)
|
|
731
|
+
if _ctx_block:
|
|
732
732
|
model_messages = list(model_messages) + [
|
|
733
|
-
{"role": "
|
|
733
|
+
{"role": "system", "content": _ctx_block}
|
|
734
734
|
]
|
|
735
735
|
except Exception:
|
|
736
736
|
pass
|
localcode/agent/prompts.py
CHANGED
|
@@ -164,9 +164,12 @@ def model_identity_line(model: str) -> str:
|
|
|
164
164
|
# to its training priors otherwise answers "I'm Gemma by Google". So: lead
|
|
165
165
|
# with LocalCode, disclose the model only on request.
|
|
166
166
|
return (
|
|
167
|
-
f"
|
|
168
|
-
f"
|
|
169
|
-
f"
|
|
167
|
+
f"Your name is LocalCode (never lead with the underlying model name). "
|
|
168
|
+
f"Do NOT introduce or re-introduce yourself. Never begin a message with "
|
|
169
|
+
f"\"I'm LocalCode\", a greeting, or a restatement of what you're doing — "
|
|
170
|
+
f"the user already knows who you are and what the task is. Just continue "
|
|
171
|
+
f"the work: take the next action directly. Only state your name if the "
|
|
172
|
+
f"user explicitly asks who you are. "
|
|
170
173
|
f"The model currently powering you is \"{friendly}\"; report it as EXACTLY "
|
|
171
174
|
f"\"{friendly}\" — and only — when the user specifically asks which "
|
|
172
175
|
f"model / version / quant you are, or asks to name something (a file, "
|
localcode/memory_guard.py
CHANGED
|
@@ -209,3 +209,61 @@ def recommended_jetsam_limit_mb() -> int:
|
|
|
209
209
|
total_mb = 16 * 1024
|
|
210
210
|
reserve = 3500 if total_mb < 24 * 1024 else 6000
|
|
211
211
|
return max(2048, total_mb - reserve)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def should_disable_metal_residency(model_path: str, ctx_size: int) -> bool:
|
|
215
|
+
"""Whether to set GGML_METAL_NO_RESIDENCY=1 for this launch.
|
|
216
|
+
|
|
217
|
+
Root cause of the recurring mid-stream SIGKILL (-9, no crash report, tens
|
|
218
|
+
of GB of system RAM still free): on Apple Silicon, llama.cpp's Metal
|
|
219
|
+
residency sets keep the model weights + KV cache + compute buffers WIRED
|
|
220
|
+
for ~3 minutes (ggml-metal-device.m). During a long generation the wired
|
|
221
|
+
total grows, and when a decode-step spike crosses the GPU's
|
|
222
|
+
`recommendedMaxWorkingSetSize` (~75% of RAM — a hard ceiling independent of
|
|
223
|
+
free system RAM), the kernel kills the process outright. This is the exact
|
|
224
|
+
bug class in ggml-org/llama.cpp#16646 on the same M4 Max / 128 GB hardware.
|
|
225
|
+
|
|
226
|
+
Setting GGML_METAL_NO_RESIDENCY=1 makes those buffers EVICTABLE instead of
|
|
227
|
+
wired, so the OS pages under pressure rather than killing — turning a hard
|
|
228
|
+
crash into (at worst) mild slowdown, and only when actually near the cap.
|
|
229
|
+
|
|
230
|
+
Gated to at-risk launches so the common small-context fast path is
|
|
231
|
+
untouched: large context (>=128K, where KV + compute-buffer growth is the
|
|
232
|
+
trigger) OR a model whose weights alone are a large fraction of the wired
|
|
233
|
+
cap. No user action, no sudo — we control the spawn env.
|
|
234
|
+
"""
|
|
235
|
+
try:
|
|
236
|
+
r = subprocess.run(["sysctl", "-n", "hw.memsize"],
|
|
237
|
+
capture_output=True, text=True, timeout=2)
|
|
238
|
+
total_mb = int(r.stdout.strip()) // (1024 * 1024)
|
|
239
|
+
except Exception:
|
|
240
|
+
return False
|
|
241
|
+
# Metal's wired ceiling is ~75% of physical RAM.
|
|
242
|
+
wired_cap_mb = int(total_mb * 0.75)
|
|
243
|
+
if ctx_size >= 131072:
|
|
244
|
+
return True
|
|
245
|
+
try:
|
|
246
|
+
import os as _os
|
|
247
|
+
model_mb = _os.path.getsize(model_path) // (1024 * 1024)
|
|
248
|
+
except Exception:
|
|
249
|
+
model_mb = 0
|
|
250
|
+
# Weights alone over ~55% of the wired cap → KV + compute will breach it.
|
|
251
|
+
return model_mb > int(wired_cap_mb * 0.55)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def metal_residency_env(model_path: str, cmd: list) -> dict:
|
|
255
|
+
"""Return {GGML_METAL_NO_RESIDENCY: '1'} when this launch is at risk of the
|
|
256
|
+
wired-cap SIGKILL, else {}. macOS-only; parses --ctx-size from the command.
|
|
257
|
+
"""
|
|
258
|
+
import sys as _sys
|
|
259
|
+
if _sys.platform != "darwin":
|
|
260
|
+
return {}
|
|
261
|
+
ctx = 0
|
|
262
|
+
for i, tok in enumerate(cmd):
|
|
263
|
+
if tok == "--ctx-size" and i + 1 < len(cmd):
|
|
264
|
+
try:
|
|
265
|
+
ctx = int(cmd[i + 1])
|
|
266
|
+
except Exception:
|
|
267
|
+
ctx = 0
|
|
268
|
+
break
|
|
269
|
+
return {"GGML_METAL_NO_RESIDENCY": "1"} if should_disable_metal_residency(model_path, ctx) else {}
|
localcode/server_manager.py
CHANGED
|
@@ -299,12 +299,10 @@ class ServerManager:
|
|
|
299
299
|
self._lock = threading.Lock()
|
|
300
300
|
self._last_exit_code: Optional[int] = None # disconnect diagnostics
|
|
301
301
|
self._last_death_was_pressure: bool = False
|
|
302
|
-
# Idle auto-suspend:
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
# The next chat call transparently respawns via _restart_server.
|
|
307
|
-
# 0 disables; default 10 min. Override with env LOCALCODE_IDLE_SUSPEND_S.
|
|
302
|
+
# Idle auto-suspend: a watchdog shuts the server down after
|
|
303
|
+
# `_idle_timeout_s` of inactivity (stops the GPU cooking the laptop
|
|
304
|
+
# while the user reads); the next chat call respawns via
|
|
305
|
+
# _restart_server. 0 disables; default 10 min. Env: LOCALCODE_IDLE_SUSPEND_S.
|
|
308
306
|
import os as _os
|
|
309
307
|
try:
|
|
310
308
|
self._idle_timeout_s: float = float(
|
|
@@ -394,10 +392,8 @@ class ServerManager:
|
|
|
394
392
|
_prev_model = self._model_path # capture before shutdown clears it
|
|
395
393
|
self._shutdown_locked()
|
|
396
394
|
# Wait for the OLD server's memory to release before spawning the
|
|
397
|
-
# new one
|
|
398
|
-
#
|
|
399
|
-
# trips the pressure monitor on a 16 GB Mac. Best-effort — targets
|
|
400
|
-
# ~11 GB free, times out at 8 s, proceeds anyway.
|
|
395
|
+
# new one (kernel takes 1-3 s to free wired Metal memory; spawning
|
|
396
|
+
# into that window double-commits). Best-effort: ~11 GB target, 8 s.
|
|
401
397
|
if had_prior:
|
|
402
398
|
# Only a genuine model change is "model_switch"; a same-model
|
|
403
399
|
# relaunch (crash/disconnect recovery) is "server_restart".
|
|
@@ -422,6 +418,15 @@ class ServerManager:
|
|
|
422
418
|
# AGX driver may read the env at first Metal touch — setting it in
|
|
423
419
|
# the parent env before spawn is the belt-and-suspenders approach.
|
|
424
420
|
env.setdefault("AGX_RELAX_CDM_CTXSTORE_TIMEOUT", "1")
|
|
421
|
+
# Disable Metal residency sets on at-risk launches → prevents the wired-cap SIGKILL.
|
|
422
|
+
try:
|
|
423
|
+
from .memory_guard import metal_residency_env
|
|
424
|
+
_renv = metal_residency_env(str(model_path), cmd)
|
|
425
|
+
if _renv:
|
|
426
|
+
env.update(_renv)
|
|
427
|
+
_lifecycle_log("metal_no_residency", model=str(model_path))
|
|
428
|
+
except Exception:
|
|
429
|
+
pass
|
|
425
430
|
# Capture the server's OWN stdout/stderr to server.log (this runtime
|
|
426
431
|
# path used to send both to /dev/null, discarding llama-server's
|
|
427
432
|
# final ggml/Metal error on the recurring -9). Append per-spawn.
|
|
@@ -461,11 +466,10 @@ class ServerManager:
|
|
|
461
466
|
except Exception:
|
|
462
467
|
pass
|
|
463
468
|
|
|
464
|
-
# Layer 2: userspace
|
|
465
|
-
# kern.memorystatus_vm_pressure_level every 500 ms
|
|
466
|
-
#
|
|
467
|
-
#
|
|
468
|
-
# pressure (everything else swelling) not just our own RSS.
|
|
469
|
+
# Layer 2: userspace pressure monitor — polls
|
|
470
|
+
# kern.memorystatus_vm_pressure_level every 500 ms and SIGTERMs our
|
|
471
|
+
# server on WARN. Complements the jetsam ceiling (acts on system-
|
|
472
|
+
# wide pressure, not just our own RSS).
|
|
469
473
|
try:
|
|
470
474
|
self._pressure_thread = start_pressure_monitor(
|
|
471
475
|
self._process,
|
|
@@ -474,10 +478,8 @@ class ServerManager:
|
|
|
474
478
|
except Exception:
|
|
475
479
|
self._pressure_thread = None
|
|
476
480
|
self._write_pid_file(self._process.pid)
|
|
477
|
-
# Log the FULL launch command
|
|
478
|
-
#
|
|
479
|
-
# cause repetition pathologies that look like model bugs. Truncated
|
|
480
|
-
# to 4000 chars (~80 args) to keep the event small.
|
|
481
|
+
# Log the FULL launch command (which flags was the server using?);
|
|
482
|
+
# truncated to 4000 chars (~80 args) to keep the event small.
|
|
481
483
|
_flags_str = " ".join(str(c) for c in cmd)[:4000]
|
|
482
484
|
_lifecycle_log("server_started", pid=self._process.pid, port=port,
|
|
483
485
|
model=Path(model_path).name,
|
|
@@ -698,10 +700,8 @@ class ServerManager:
|
|
|
698
700
|
_lifecycle_log("pressure_kill", level=level, pid=killed_pid,
|
|
699
701
|
guard="memory_pressure", exit_code=self._last_exit_code,
|
|
700
702
|
free_mb=_system_free_memory_mb())
|
|
701
|
-
# Clear internal state so
|
|
702
|
-
#
|
|
703
|
-
# the pressure-monitor thread because `_process` reads/writes
|
|
704
|
-
# are atomic in CPython for the assignment.
|
|
703
|
+
# Clear internal state so callers see "no server" not a phantom Popen.
|
|
704
|
+
# Safe from the pressure-monitor thread (assignment is atomic in CPython).
|
|
705
705
|
self._process = None
|
|
706
706
|
self._model_path = None
|
|
707
707
|
|
localcode/tools/write_file.py
CHANGED
|
@@ -173,6 +173,19 @@ def execute(ctx: ToolContext, args: dict) -> str:
|
|
|
173
173
|
# catches the data.py-style "model copies in-memory stub back into
|
|
174
174
|
# write_file" disaster, which was the one case where rejecting
|
|
175
175
|
# was load-bearing.
|
|
176
|
+
# Log the EXACT content the model sent (post-JSON-parse) so a "the file is
|
|
177
|
+
# corrupted / write_file isn't writing what I sent" report is provable from
|
|
178
|
+
# logs in one grep, instead of a mystery. Best-effort, capped, append-only.
|
|
179
|
+
try:
|
|
180
|
+
from ..paths import global_state_dir
|
|
181
|
+
_wl = global_state_dir() / "write_content.log"
|
|
182
|
+
with open(_wl, "a", encoding="utf-8", errors="replace") as _fh:
|
|
183
|
+
_fh.write(f"\n===== write_file {args['path']} ({len(content)} chars) =====\n")
|
|
184
|
+
_fh.write(content[:20000])
|
|
185
|
+
_fh.write("\n")
|
|
186
|
+
except Exception:
|
|
187
|
+
pass
|
|
188
|
+
|
|
176
189
|
existed = path.is_file()
|
|
177
190
|
# Capture the prior content so an in-place rewrite renders as a diff card
|
|
178
191
|
# (like edit_file/multi_edit), not a bare "Rewrote (N lines)" one-liner.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
localcode/__init__.py,sha256=
|
|
1
|
+
localcode/__init__.py,sha256=1BYETer-qr6n0hQRQuRG08uTZrtp262X5k87-OyEhp0,49
|
|
2
2
|
localcode/__main__.py,sha256=_4Tblte9F1KOpB-oGBQuTlaSN-IncLmhihqhBkpH3pU,69
|
|
3
3
|
localcode/_subproc_env.py,sha256=eltq_kKvqGj22pIFivB5BZft9-s5fziBW0lsV9FhEV4,2383
|
|
4
4
|
localcode/app.py,sha256=FSwpce6Jfn7zJmhL1ipaqTq0xKh0MxCMnEnMQuFV8L8,67820
|
|
@@ -30,7 +30,7 @@ localcode/injection_defense.py,sha256=sggmoowHEAKYV3v-1PTWtv8KjekEL0kZ0-O03hb3Dm
|
|
|
30
30
|
localcode/launcher.py,sha256=uDsIy66q-VexbvdIb7iwzYwDmmYhWRP10SNX4GiEWQ4,11462
|
|
31
31
|
localcode/logging_utils.py,sha256=mFn_LO26IkT7OLxKs1ke4mND8BJeDLf-UULCYSSxyz0,920
|
|
32
32
|
localcode/lsp.py,sha256=WTtdbdgforZdWMRqlzCQp2ndkKRbfIE1f5GfGeRH1xU,5939
|
|
33
|
-
localcode/memory_guard.py,sha256=
|
|
33
|
+
localcode/memory_guard.py,sha256=9OBpvzOGP_gzYvG3sXhiYr6vzTJRW_YtRDaccouSIPU,11390
|
|
34
34
|
localcode/model_config.py,sha256=4iPw5M6tnjMvdILtWlRkOdE6QMFRJCAIYBmadc82ZjQ,14261
|
|
35
35
|
localcode/model_families.py,sha256=mEBIoEMe-qL93Twxm-4mYv76dWEkKVp5CiQlY43mkWA,11203
|
|
36
36
|
localcode/models.py,sha256=fetDQSmOR3GrMMv_H9ecAZSmHa2TPhsYw8eSYKDl5YA,8243
|
|
@@ -48,7 +48,7 @@ localcode/recommendations.py,sha256=HVh8u2L3xbwc2qxH7Syec2cEtlEoFV9n1c33SqWF3ng,
|
|
|
48
48
|
localcode/recovery.py,sha256=QUSsBA9IS5AJ6W4m4nIv2RaktTO9TJeL0RcDeUSWHJk,5237
|
|
49
49
|
localcode/runtime.py,sha256=UifjZAe8kPgnMCk4N2V2eX4VY6Us6IYmDwbvVwMhwC0,115816
|
|
50
50
|
localcode/runtime_diffusion.py,sha256=bVopikSdQeaV9mAuzWYoXtoXpUb7U0yMXU5VFTpbsBc,39741
|
|
51
|
-
localcode/server_manager.py,sha256=
|
|
51
|
+
localcode/server_manager.py,sha256=9xHqShGB9rVr62qhTwWkYZDX4KOuxACvujhFC0ztYdM,34305
|
|
52
52
|
localcode/session.py,sha256=taEyOOiCcNXEr8oOeszyWvzvn-oAZ7knXQ7BxRew08Q,11970
|
|
53
53
|
localcode/shell.py,sha256=2QcXk0NB6d8t2gpbbKOB5nGZ_xBJ7VVsLtF4hnUknVM,2300
|
|
54
54
|
localcode/skills.py,sha256=TGdy63dQTlfa6U2l7wjg3zSatKyLAYdu_wVO-J2BrHY,25800
|
|
@@ -71,9 +71,9 @@ localcode/agent/context.py,sha256=adV_WlFBHMDHsWdndRgrkZqRweDD9plyqMP_2X74WAw,37
|
|
|
71
71
|
localcode/agent/goal.py,sha256=X87Q3rsdp70wOfkpbh35M4qFWSNyklX3RuRWjv-pCXs,6264
|
|
72
72
|
localcode/agent/helpers.py,sha256=vutFyCtnca0UnF0wgk7LdsqVWdiqyYm4YnFOxtnCJ_A,15004
|
|
73
73
|
localcode/agent/hooks.py,sha256=XadEbSM7gHtKlLrqSKnvcQFcoWoNUXdlMdIPS-EwBGA,15841
|
|
74
|
-
localcode/agent/loop.py,sha256=
|
|
74
|
+
localcode/agent/loop.py,sha256=2O2iKFsCQmr1CoQq40Ls_NeBs7tFgcDVfkU8YhqXXdM,99864
|
|
75
75
|
localcode/agent/prompt_context.py,sha256=abJPLMhTzuVcsW4AfMBIW-SEqmrU69DhI-0vGZllZVU,13707
|
|
76
|
-
localcode/agent/prompts.py,sha256=
|
|
76
|
+
localcode/agent/prompts.py,sha256=LCJBM2mV_SKc1OXtH0-ih9OPQnhrTh3vTlrOgLUiVDA,14845
|
|
77
77
|
localcode/agent/recovery.py,sha256=oCghDm9BdHhvl98Kx8EikNuxSk4Geh-0cgAEhTpH-sw,16738
|
|
78
78
|
localcode/agent/sections.py,sha256=9hnwaNritQHgn7qIGFBZ3ChZgMQXQcqtmpdbR1wafA0,8998
|
|
79
79
|
localcode/agent/streaming.py,sha256=TEcKbmKfDGV1razrV-ceVrvIIxvS-GmLWGNDnF0gSTA,12872
|
|
@@ -114,7 +114,7 @@ localcode/tools/skill_tool.py,sha256=vTVPyRWxICtDCVYWMiAMInGnc-wGAk2L1EZn-6e79nE
|
|
|
114
114
|
localcode/tools/todo_write.py,sha256=8A4k9MzoqOtzJ6y2Gd3lEGkINcpV3JVCd2lYBmdd6qI,5801
|
|
115
115
|
localcode/tools/web_fetch.py,sha256=UPhMrLipwN5xJx5-zr634gL4p6nIRqYI2_7tbUw_8Go,2770
|
|
116
116
|
localcode/tools/web_search.py,sha256=ILfBBx50p00OjA939HzdDmdao4QLL8sRuxlBoUDjmC8,1122
|
|
117
|
-
localcode/tools/write_file.py,sha256=
|
|
117
|
+
localcode/tools/write_file.py,sha256=2rfbp0j_ApHuk8fUAopMl6H1BIyEMKWKXjZ__xrY6oI,9743
|
|
118
118
|
localcode/tui/__init__.py,sha256=Yfy9bvw3l1iY4FDhRX8qZVgnZLuYiq8lc9SDYl4cWwc,61
|
|
119
119
|
localcode/tui/app.py,sha256=KjQzpGKnxkgh-0kVc3zcb63Q6a5LBuGreBqTUrasiqM,17700
|
|
120
120
|
localcode/tui/bridge.py,sha256=urPPCqC_nisAYawWk3juvMI8IaoJfv4g7USpGOZOqQc,2803
|
|
@@ -131,9 +131,9 @@ localcode/tui/widgets/chat_log.py,sha256=D5DUkrQXL_ilxyC8N1xIPLw4Pqeh0jSWt1mPgho
|
|
|
131
131
|
localcode/tui/widgets/voice_visualizer.py,sha256=zOhrIxaMcg-F2Y8LvHJTddq227phmiTjtEyRbkx1XxU,4709
|
|
132
132
|
localcode/tui/widgets/messages/__init__.py,sha256=952AZ1qVMGUIqPIry7mwxnYbMkgPlcY5FAwZtEa5YPg,967
|
|
133
133
|
localcode/tui/widgets/messages/diff.py,sha256=khDf_MThrQz0S62-t-m7i3OU5cdNolUk1TCeKMRPDQI,10910
|
|
134
|
-
localcode-0.3.
|
|
135
|
-
localcode-0.3.
|
|
136
|
-
localcode-0.3.
|
|
137
|
-
localcode-0.3.
|
|
138
|
-
localcode-0.3.
|
|
139
|
-
localcode-0.3.
|
|
134
|
+
localcode-0.3.5.dist-info/licenses/LICENSE,sha256=Ahg0cteZ-6bIRB7KC15WEtdqWnzy8HY_yzsZBoVRql0,11294
|
|
135
|
+
localcode-0.3.5.dist-info/METADATA,sha256=F80qNJ73o16hDwmaZzV6e9B-EGuc3ARhiHgwg_P0HDo,7813
|
|
136
|
+
localcode-0.3.5.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
137
|
+
localcode-0.3.5.dist-info/entry_points.txt,sha256=rE7elTGUvMsBd-Quy22cuTVJ-zGVOmx0nEuC6ni9Tg4,87
|
|
138
|
+
localcode-0.3.5.dist-info/top_level.txt,sha256=58eL3Rw8v0OGmYNxjx8uS4ERWatrzUbyCL5inlukkTo,10
|
|
139
|
+
localcode-0.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|