wafer-cli 0.2.38__tar.gz → 0.2.39__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.
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/PKG-INFO +1 -1
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/pyproject.toml +1 -1
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/agent_defaults.py +48 -1
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/cli.py +52 -11
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/wevin_cli.py +36 -3
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/PKG-INFO +1 -1
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/README.md +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/setup.cfg +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_analytics.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_auth.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_billing.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_cli_coverage.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_cli_parity_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_config_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_file_operations_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_kernel_scope_cli.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_nsys_analyze.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_nsys_profile.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_output.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_rocprof_compute_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_skill_commands.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_ssh_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_targets_ops.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_wevin_cli.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/tests/test_workflow_integration.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/GUIDE.md +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/__init__.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/analytics.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/api_client.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/auth.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/autotuner.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/baseline.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/billing.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/cli_instructions.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/config.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/corpus.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/evaluate.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/global_config.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/gpu_run.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/inference.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/kernel_scope.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/ncu_analyze.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/nsys_analyze.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/nsys_profile.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/output.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/problems.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/rocprof_compute.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/rocprof_sdk.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/rocprof_systems.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/skills/wafer-guide/SKILL.md +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/specs_cli.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/ssh_keys.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/target_lock.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/targets.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/targets_cli.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/targets_ops.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/__init__.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/aiter_optimize.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/ask_docs.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/optimize_kernel.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/optimize_kernelbench.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/optimize_vllm.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/templates/trace_analyze.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/tests/test_eval_cli_parity.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/trace_compare.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/tracelens.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer/workspaces.py +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/SOURCES.txt +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/dependency_links.txt +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/entry_points.txt +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/requires.txt +0 -0
- {wafer_cli-0.2.38 → wafer_cli-0.2.39}/wafer_cli.egg-info/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Shared agent defaults for kernel
|
|
1
|
+
"""Shared agent defaults for kernel tasks.
|
|
2
2
|
|
|
3
3
|
Single source of truth for bash allowlists and enabled tools used by both:
|
|
4
4
|
- CLI templates (apps/wafer-cli/wafer/templates/*.py)
|
|
@@ -195,3 +195,50 @@ VLLM_BASH_ALLOWLIST: list[str] = [
|
|
|
195
195
|
"cd",
|
|
196
196
|
"git",
|
|
197
197
|
]
|
|
198
|
+
|
|
199
|
+
# Tools available to audit agents (read-only + bash for compilation/profiling)
|
|
200
|
+
AUDIT_ENABLED_TOOLS: list[str] = ["read", "glob", "grep", "bash"]
|
|
201
|
+
|
|
202
|
+
# Bash commands allowed for kernel audit agents.
|
|
203
|
+
AUDIT_BASH_ALLOWLIST: list[str] = [
|
|
204
|
+
# Read-only
|
|
205
|
+
"ls",
|
|
206
|
+
"cat",
|
|
207
|
+
"head",
|
|
208
|
+
"tail",
|
|
209
|
+
"wc",
|
|
210
|
+
"find",
|
|
211
|
+
"grep",
|
|
212
|
+
"rg",
|
|
213
|
+
"pwd",
|
|
214
|
+
"tree",
|
|
215
|
+
"which",
|
|
216
|
+
"diff",
|
|
217
|
+
"sort",
|
|
218
|
+
# Filesystem
|
|
219
|
+
"mkdir",
|
|
220
|
+
# Compilation
|
|
221
|
+
"make",
|
|
222
|
+
"cmake",
|
|
223
|
+
"nvcc",
|
|
224
|
+
"hipcc",
|
|
225
|
+
"g++",
|
|
226
|
+
"gcc",
|
|
227
|
+
"clang",
|
|
228
|
+
"python",
|
|
229
|
+
"python3",
|
|
230
|
+
# Execution — allows running compiled binaries via ./path.
|
|
231
|
+
# Security note: the agent can already compile arbitrary code via hipcc/gcc/etc,
|
|
232
|
+
# so blocking ./ execution doesn't add meaningful protection.
|
|
233
|
+
"./",
|
|
234
|
+
# Profiling
|
|
235
|
+
"wafer evaluate",
|
|
236
|
+
"wafer nvidia ncu",
|
|
237
|
+
"wafer nvidia nsys",
|
|
238
|
+
"wafer amd rocprof-compute",
|
|
239
|
+
"wafer amd rocprof-sdk",
|
|
240
|
+
"wafer amd rocprof-systems",
|
|
241
|
+
"wafer compiler-analyze",
|
|
242
|
+
# Misc
|
|
243
|
+
"timeout",
|
|
244
|
+
]
|
|
@@ -1862,6 +1862,12 @@ def kernelbench_evaluate( # noqa: PLR0913, PLR0915
|
|
|
1862
1862
|
help="Sync files and generate eval script but don't run. "
|
|
1863
1863
|
"Prints the command to run manually (useful for wrapping with rocprof, etc.)",
|
|
1864
1864
|
),
|
|
1865
|
+
pool_timeout: int = typer.Option(
|
|
1866
|
+
600,
|
|
1867
|
+
"--pool-timeout",
|
|
1868
|
+
help="Seconds to wait for a target from the pool before failing (default: 600). "
|
|
1869
|
+
"Set to 0 for immediate failure if all targets are busy.",
|
|
1870
|
+
),
|
|
1865
1871
|
json_output: bool = typer.Option(
|
|
1866
1872
|
False, "--json", help="Output as single JSON object (machine-readable)"
|
|
1867
1873
|
),
|
|
@@ -1953,14 +1959,24 @@ def kernelbench_evaluate( # noqa: PLR0913, PLR0915
|
|
|
1953
1959
|
collector.finalize()
|
|
1954
1960
|
raise typer.Exit(1) from None
|
|
1955
1961
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1962
|
+
effective_timeout = pool_timeout if pool_timeout > 0 else None
|
|
1963
|
+
collector.emit("pool_acquire", pool=pool, count=len(usable_targets), timeout=pool_timeout)
|
|
1964
|
+
pool_lock_context = acquire_from_pool(usable_targets, timeout=effective_timeout)
|
|
1958
1965
|
acquired_target = pool_lock_context.__enter__()
|
|
1959
1966
|
|
|
1960
1967
|
if acquired_target is None:
|
|
1961
1968
|
# Exit context manager before raising to avoid resource leak
|
|
1962
1969
|
pool_lock_context.__exit__(None, None, None)
|
|
1963
|
-
|
|
1970
|
+
if pool_timeout > 0:
|
|
1971
|
+
collector.set_error(
|
|
1972
|
+
"pool",
|
|
1973
|
+
"AllTargetsBusy",
|
|
1974
|
+
pool=pool,
|
|
1975
|
+
targets=usable_targets,
|
|
1976
|
+
message=f"All targets busy after waiting {pool_timeout}s",
|
|
1977
|
+
)
|
|
1978
|
+
else:
|
|
1979
|
+
collector.set_error("pool", "AllTargetsBusy", pool=pool, targets=usable_targets)
|
|
1964
1980
|
collector.finalize()
|
|
1965
1981
|
raise typer.Exit(1)
|
|
1966
1982
|
|
|
@@ -2315,6 +2331,12 @@ def gpumode_evaluate( # noqa: PLR0913, PLR0915
|
|
|
2315
2331
|
True, "--sync-artifacts/--no-sync-artifacts", help="Download artifacts"
|
|
2316
2332
|
),
|
|
2317
2333
|
gpu_id: int | None = typer.Option(None, "--gpu-id", help="Override GPU ID"),
|
|
2334
|
+
pool_timeout: int = typer.Option(
|
|
2335
|
+
600,
|
|
2336
|
+
"--pool-timeout",
|
|
2337
|
+
help="Seconds to wait for a target from the pool before failing (default: 600). "
|
|
2338
|
+
"Set to 0 for immediate failure if all targets are busy.",
|
|
2339
|
+
),
|
|
2318
2340
|
) -> None:
|
|
2319
2341
|
"""Run kernel evaluation in GPUMode format (functional).
|
|
2320
2342
|
|
|
@@ -2394,14 +2416,21 @@ def gpumode_evaluate( # noqa: PLR0913, PLR0915
|
|
|
2394
2416
|
typer.echo(" Run 'wafer auth status' to see which providers need setup.", err=True)
|
|
2395
2417
|
raise typer.Exit(1) from None
|
|
2396
2418
|
|
|
2419
|
+
effective_timeout = pool_timeout if pool_timeout > 0 else None
|
|
2397
2420
|
typer.echo(f"Acquiring target from pool '{pool}' ({len(usable_targets)} targets)...")
|
|
2398
|
-
pool_lock_context = acquire_from_pool(usable_targets)
|
|
2421
|
+
pool_lock_context = acquire_from_pool(usable_targets, timeout=effective_timeout)
|
|
2399
2422
|
acquired_target = pool_lock_context.__enter__()
|
|
2400
2423
|
|
|
2401
2424
|
if acquired_target is None:
|
|
2402
2425
|
# Exit context manager before raising to avoid resource leak
|
|
2403
2426
|
pool_lock_context.__exit__(None, None, None)
|
|
2404
|
-
|
|
2427
|
+
if pool_timeout > 0:
|
|
2428
|
+
typer.echo(
|
|
2429
|
+
f"Error: All targets in pool '{pool}' are busy (waited {pool_timeout}s)",
|
|
2430
|
+
err=True,
|
|
2431
|
+
)
|
|
2432
|
+
else:
|
|
2433
|
+
typer.echo(f"Error: All targets in pool '{pool}' are busy", err=True)
|
|
2405
2434
|
typer.echo(f" Targets: {', '.join(usable_targets)}", err=True)
|
|
2406
2435
|
raise typer.Exit(1)
|
|
2407
2436
|
|
|
@@ -7751,16 +7780,24 @@ def compare_analyze(
|
|
|
7751
7780
|
"-f",
|
|
7752
7781
|
help="Output format: text, text-layers, csv, csv-layers, json",
|
|
7753
7782
|
),
|
|
7754
|
-
output: Path | None = typer.Option(
|
|
7783
|
+
output: Path | None = typer.Option(
|
|
7784
|
+
None, "--output", "-o", help="Output file (default: stdout)"
|
|
7785
|
+
),
|
|
7755
7786
|
phase: str = typer.Option(
|
|
7756
7787
|
"all",
|
|
7757
7788
|
"--phase",
|
|
7758
7789
|
help="Filter by phase: all, prefill, decode",
|
|
7759
7790
|
),
|
|
7760
7791
|
layers: bool = typer.Option(False, "--layers", help="Show layer-wise performance breakdown"),
|
|
7761
|
-
all: bool = typer.Option(
|
|
7762
|
-
|
|
7763
|
-
|
|
7792
|
+
all: bool = typer.Option(
|
|
7793
|
+
False, "--all", help="Show all items (no truncation for layers, operations, kernels)"
|
|
7794
|
+
),
|
|
7795
|
+
stack_traces: bool = typer.Option(
|
|
7796
|
+
False, "--stack-traces", help="Show Python stack traces for operations"
|
|
7797
|
+
),
|
|
7798
|
+
json: bool = typer.Option(
|
|
7799
|
+
False, "--json", hidden=True, help="Ignored (for compatibility with cliExecutor)"
|
|
7800
|
+
),
|
|
7764
7801
|
) -> None:
|
|
7765
7802
|
"""Compare GPU traces from two platforms platforms.
|
|
7766
7803
|
|
|
@@ -7824,13 +7861,17 @@ def compare_fusion_cmd(
|
|
|
7824
7861
|
"-f",
|
|
7825
7862
|
help="Output format: text, csv, json",
|
|
7826
7863
|
),
|
|
7827
|
-
output: Path | None = typer.Option(
|
|
7864
|
+
output: Path | None = typer.Option(
|
|
7865
|
+
None, "--output", "-o", help="Output file (default: stdout)"
|
|
7866
|
+
),
|
|
7828
7867
|
min_group_size: int = typer.Option(
|
|
7829
7868
|
50,
|
|
7830
7869
|
"--min-group-size",
|
|
7831
7870
|
help="Minimum correlation group size to analyze",
|
|
7832
7871
|
),
|
|
7833
|
-
json: bool = typer.Option(
|
|
7872
|
+
json: bool = typer.Option(
|
|
7873
|
+
False, "--json", hidden=True, help="Ignored (for compatibility with cliExecutor)"
|
|
7874
|
+
),
|
|
7834
7875
|
) -> None:
|
|
7835
7876
|
"""Analyze kernel fusion differences between AMD and NVIDIA traces.
|
|
7836
7877
|
|
|
@@ -326,13 +326,28 @@ def _build_environment(
|
|
|
326
326
|
tools_override: list[str] | None,
|
|
327
327
|
corpus_path: str | None,
|
|
328
328
|
no_sandbox: bool = False,
|
|
329
|
+
has_target: bool = False,
|
|
330
|
+
template_args: dict[str, str] | None = None,
|
|
329
331
|
) -> Environment:
|
|
330
|
-
"""Build a CodingEnvironment from template config.
|
|
332
|
+
"""Build a CodingEnvironment from template config.
|
|
333
|
+
|
|
334
|
+
Working directory priority:
|
|
335
|
+
1. Template arg "dir" (--args dir=./my_project) — scopes agent to a directory
|
|
336
|
+
2. corpus_path (--corpus cuda) — for doc-browsing templates
|
|
337
|
+
3. Current working directory
|
|
338
|
+
"""
|
|
331
339
|
from wafer_core.environments.coding import CodingEnvironment
|
|
332
340
|
from wafer_core.rollouts.templates import DANGEROUS_BASH_COMMANDS
|
|
333
341
|
from wafer_core.sandbox import SandboxMode
|
|
334
342
|
|
|
335
|
-
|
|
343
|
+
# Template arg "dir" takes priority over corpus_path
|
|
344
|
+
dir_arg = (template_args or {}).get("dir")
|
|
345
|
+
if dir_arg:
|
|
346
|
+
working_dir = Path(dir_arg).resolve()
|
|
347
|
+
elif corpus_path:
|
|
348
|
+
working_dir = Path(corpus_path)
|
|
349
|
+
else:
|
|
350
|
+
working_dir = Path.cwd()
|
|
336
351
|
resolved_tools = list(tools_override or tpl.tools)
|
|
337
352
|
|
|
338
353
|
# Add skill tool if skills are enabled
|
|
@@ -340,12 +355,18 @@ def _build_environment(
|
|
|
340
355
|
resolved_tools.append("skill")
|
|
341
356
|
|
|
342
357
|
sandbox_mode = SandboxMode.DISABLED if no_sandbox else SandboxMode.ENABLED
|
|
358
|
+
|
|
359
|
+
# Enable network when a target is configured — the agent needs to reach
|
|
360
|
+
# remote GPUs via SSH/HTTPS. Filesystem sandbox stays enforced.
|
|
361
|
+
allow_network = has_target
|
|
362
|
+
|
|
343
363
|
env: Environment = CodingEnvironment(
|
|
344
364
|
working_dir=working_dir,
|
|
345
365
|
enabled_tools=resolved_tools,
|
|
346
366
|
bash_allowlist=tpl.bash_allowlist,
|
|
347
367
|
bash_denylist=DANGEROUS_BASH_COMMANDS,
|
|
348
368
|
sandbox_mode=sandbox_mode,
|
|
369
|
+
allow_network=allow_network,
|
|
349
370
|
) # type: ignore[assignment]
|
|
350
371
|
return env
|
|
351
372
|
|
|
@@ -589,9 +610,21 @@ def main( # noqa: PLR0913, PLR0915
|
|
|
589
610
|
# CLI args override template values
|
|
590
611
|
resolved_single_turn = single_turn if single_turn is not None else tpl.single_turn
|
|
591
612
|
|
|
613
|
+
# Check if a default target is configured — if so, enable network access
|
|
614
|
+
# so the agent can reach remote GPUs via SSH/HTTPS.
|
|
615
|
+
has_target = False
|
|
616
|
+
try:
|
|
617
|
+
from wafer.targets import get_default_target
|
|
618
|
+
|
|
619
|
+
has_target = get_default_target() is not None
|
|
620
|
+
except Exception:
|
|
621
|
+
pass # No target configured — network stays disabled
|
|
622
|
+
|
|
592
623
|
# Build endpoint and environment
|
|
593
624
|
endpoint = _build_endpoint(tpl, model, api_base, api_key, api_key_refresh)
|
|
594
|
-
environment = _build_environment(
|
|
625
|
+
environment = _build_environment(
|
|
626
|
+
tpl, tools, corpus_path, no_sandbox, has_target=has_target, template_args=template_args
|
|
627
|
+
)
|
|
595
628
|
|
|
596
629
|
# Session store
|
|
597
630
|
session_store = FileSessionStore()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|