apmw 0.1.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.
- apmw/__init__.py +443 -0
- apmw/data/agent-strip-fields.json +7 -0
- apmw-0.1.0.dist-info/METADATA +5 -0
- apmw-0.1.0.dist-info/RECORD +6 -0
- apmw-0.1.0.dist-info/WHEEL +4 -0
- apmw-0.1.0.dist-info/entry_points.txt +2 -0
apmw/__init__.py
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import platform as _platform
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
TARGET_DIRS = {
|
|
13
|
+
"opencode": ".opencode",
|
|
14
|
+
"copilot": ".github",
|
|
15
|
+
"claude": ".claude",
|
|
16
|
+
"cursor": ".cursor",
|
|
17
|
+
"codex": ".codex",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Which frontmatter fields to drop per target platform. GravionLabs/apm-hub
|
|
21
|
+
# keeps an equivalent copy at config/agent-strip-fields.json (shared with
|
|
22
|
+
# src/generators/agent-target-generator.ts there) — update both if the set
|
|
23
|
+
# of fields per platform changes.
|
|
24
|
+
#
|
|
25
|
+
# Note on "claude": Claude Code accepts only name/description/tools/model;
|
|
26
|
+
# source `tools` arrays use Copilot names and `model` values are rarely
|
|
27
|
+
# valid Anthropic ids, so both are stripped — Claude then inherits the
|
|
28
|
+
# session model.
|
|
29
|
+
_STRIP_FIELDS_PATH = Path(__file__).resolve().parent / "data" / "agent-strip-fields.json"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _load_target_strip_fields() -> dict[str, list[str]]:
|
|
33
|
+
if _STRIP_FIELDS_PATH.is_file():
|
|
34
|
+
return json.loads(_STRIP_FIELDS_PATH.read_text())
|
|
35
|
+
return {
|
|
36
|
+
"opencode": ["tools", "user-invocable", "agents"],
|
|
37
|
+
"copilot": ["mode", "steps", "color", "permission", "task"],
|
|
38
|
+
"claude": ["mode", "steps", "color", "tools", "permission", "user-invocable", "agents", "model"],
|
|
39
|
+
"cursor": ["mode", "steps", "color", "permission"],
|
|
40
|
+
"codex": ["mode", "steps", "color", "permission"],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
TARGET_STRIP_FIELDS = _load_target_strip_fields()
|
|
45
|
+
|
|
46
|
+
GLOBAL_PATHS: dict[str, dict[str, list[str]]] = {
|
|
47
|
+
"opencode": {
|
|
48
|
+
"Linux": ["~/.config/opencode/agents"],
|
|
49
|
+
"Darwin": ["~/Library/Application Support/opencode/agents"],
|
|
50
|
+
"Windows": ["%APPDATA%\\opencode\\agents"],
|
|
51
|
+
},
|
|
52
|
+
"copilot": {
|
|
53
|
+
"Linux": ["~/.config/github-copilot/agents"],
|
|
54
|
+
"Darwin": ["~/Library/Application Support/github-copilot/agents"],
|
|
55
|
+
"Windows": ["%APPDATA%\\GitHub Copilot\\agents"],
|
|
56
|
+
},
|
|
57
|
+
"claude": {
|
|
58
|
+
"Linux": ["~/.claude/agents", "~/.config/claude/agents"],
|
|
59
|
+
"Darwin": ["~/Library/Application Support/claude/agents"],
|
|
60
|
+
"Windows": ["%APPDATA%\\Claude\\agents"],
|
|
61
|
+
},
|
|
62
|
+
"cursor": {
|
|
63
|
+
"Linux": ["~/.cursor/agents", "~/.config/cursor/agents"],
|
|
64
|
+
"Darwin": ["~/Library/Application Support/cursor/agents"],
|
|
65
|
+
"Windows": ["%APPDATA%\\Cursor\\agents"],
|
|
66
|
+
},
|
|
67
|
+
"codex": {
|
|
68
|
+
"Linux": ["~/.codex/agents"],
|
|
69
|
+
"Darwin": ["~/.codex/agents"],
|
|
70
|
+
"Windows": ["%APPDATA%\\Codex\\agents"],
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
TOOLS_ARRAY_RE = re.compile(r'^tools:\s*\[')
|
|
75
|
+
TOOLS_BLOCK_RE = re.compile(r'^tools:\s*$')
|
|
76
|
+
PERMISSION_RE = re.compile(r'^permission:')
|
|
77
|
+
METADATA_RE = re.compile(r'^metadata\s*:')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def resolve_path(raw: str) -> Path:
|
|
81
|
+
expanded = os.path.expandvars(os.path.expanduser(raw))
|
|
82
|
+
return Path(expanded)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_global_agents_dirs(target: str) -> list[Path]:
|
|
86
|
+
system = _platform.system()
|
|
87
|
+
dirs = GLOBAL_PATHS.get(target, {}).get(system, [])
|
|
88
|
+
return [resolve_path(d) for d in dirs]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _add_sanitize_args(parser: argparse.ArgumentParser) -> None:
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"-p", "--platform",
|
|
94
|
+
default=None,
|
|
95
|
+
choices=list(TARGET_STRIP_FIELDS.keys()),
|
|
96
|
+
help="Target platform (default: all known platforms)",
|
|
97
|
+
)
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"-s", "--source",
|
|
100
|
+
action="store_true",
|
|
101
|
+
help="Also add permission block to source agent files under packages/",
|
|
102
|
+
)
|
|
103
|
+
parser.add_argument(
|
|
104
|
+
"-g", "--global",
|
|
105
|
+
action="store_true",
|
|
106
|
+
dest="global_",
|
|
107
|
+
help="Sanitize global installation paths instead of CWD",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
112
|
+
parser = argparse.ArgumentParser(
|
|
113
|
+
prog="apmw",
|
|
114
|
+
description="Wrapper for the apm CLI that sanitizes agent frontmatter per target platform.",
|
|
115
|
+
)
|
|
116
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
117
|
+
|
|
118
|
+
install_parser = subparsers.add_parser(
|
|
119
|
+
"install", help="Run apm install then sanitize agent frontmatter per target."
|
|
120
|
+
)
|
|
121
|
+
_add_sanitize_args(install_parser)
|
|
122
|
+
|
|
123
|
+
sanitize_parser = subparsers.add_parser(
|
|
124
|
+
"sanitize", help="Sanitize an already-installed agent directory without running apm."
|
|
125
|
+
)
|
|
126
|
+
_add_sanitize_args(sanitize_parser)
|
|
127
|
+
|
|
128
|
+
update_parser = subparsers.add_parser(
|
|
129
|
+
"update", help="Run apm update then sanitize agent frontmatter per target."
|
|
130
|
+
)
|
|
131
|
+
_add_sanitize_args(update_parser)
|
|
132
|
+
update_parser.add_argument(
|
|
133
|
+
"-y", "--yes",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Skip the consent prompt (passed as --yes to apm update)",
|
|
136
|
+
)
|
|
137
|
+
update_parser.add_argument(
|
|
138
|
+
"--dry-run",
|
|
139
|
+
action="store_true",
|
|
140
|
+
help="Preview changes without writing (passed to apm update, skips sanitization)",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return parser
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def find_frontmatter(lines: list[str]) -> tuple[int, int] | None:
|
|
147
|
+
if len(lines) < 3 or lines[0].strip() != "---":
|
|
148
|
+
return None
|
|
149
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
150
|
+
if line.strip() == "---":
|
|
151
|
+
return (0, i)
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def has_array_tools(fm_lines: list[str]) -> bool:
|
|
156
|
+
for line in fm_lines:
|
|
157
|
+
if TOOLS_ARRAY_RE.match(line) or TOOLS_BLOCK_RE.match(line):
|
|
158
|
+
return True
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def strip_fields(fm_lines: list[str], fields: list[str]) -> list[str] | None:
|
|
163
|
+
patterns = [re.compile(r'^' + re.escape(f) + r'\s*:') for f in fields]
|
|
164
|
+
|
|
165
|
+
new_lines: list[str] = []
|
|
166
|
+
skip_indent = False
|
|
167
|
+
skip_indent_level = 0
|
|
168
|
+
|
|
169
|
+
for line in fm_lines:
|
|
170
|
+
if skip_indent:
|
|
171
|
+
if line and not line[0].isspace():
|
|
172
|
+
skip_indent = False
|
|
173
|
+
elif line and line[0].isspace():
|
|
174
|
+
indent = len(line) - len(line.lstrip())
|
|
175
|
+
if indent <= skip_indent_level:
|
|
176
|
+
skip_indent = False
|
|
177
|
+
else:
|
|
178
|
+
continue
|
|
179
|
+
else:
|
|
180
|
+
skip_indent = False
|
|
181
|
+
|
|
182
|
+
matched = False
|
|
183
|
+
for pat in patterns:
|
|
184
|
+
if pat.match(line):
|
|
185
|
+
matched = True
|
|
186
|
+
skip_indent = True
|
|
187
|
+
skip_indent_level = len(line) - len(line.lstrip()) if line else 0
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
if not matched:
|
|
191
|
+
new_lines.append(line)
|
|
192
|
+
|
|
193
|
+
if len(new_lines) == len(fm_lines):
|
|
194
|
+
return None
|
|
195
|
+
return new_lines
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def add_permission_block(fm_lines: list[str]) -> list[str] | None:
|
|
199
|
+
for line in fm_lines:
|
|
200
|
+
if PERMISSION_RE.match(line):
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
tool_names: list[str] = []
|
|
204
|
+
in_block = False
|
|
205
|
+
for line in fm_lines:
|
|
206
|
+
if in_block:
|
|
207
|
+
m = re.match(r'^\s+-\s+(.+)$', line)
|
|
208
|
+
if m:
|
|
209
|
+
tool_names.append(m.group(1).strip())
|
|
210
|
+
else:
|
|
211
|
+
break
|
|
212
|
+
elif TOOLS_ARRAY_RE.match(line):
|
|
213
|
+
m = TOOLS_ARRAY_RE.match(line)
|
|
214
|
+
rest = line[m.end():]
|
|
215
|
+
if rest.endswith("]"):
|
|
216
|
+
rest = rest[:-1]
|
|
217
|
+
tool_names = [t.strip().strip("'\"") for t in rest.split(",") if t.strip()]
|
|
218
|
+
break
|
|
219
|
+
elif TOOLS_BLOCK_RE.match(line):
|
|
220
|
+
in_block = True
|
|
221
|
+
|
|
222
|
+
if not tool_names:
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
insert_at = None
|
|
226
|
+
for i, line in enumerate(fm_lines):
|
|
227
|
+
if line.strip().startswith("# Copilot/APM fields") or line.strip().startswith("# Copilot"):
|
|
228
|
+
insert_at = i
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
perm_block = ["permission:"]
|
|
232
|
+
for name in tool_names:
|
|
233
|
+
perm_block.append(f" {name}: allow")
|
|
234
|
+
|
|
235
|
+
if insert_at is not None:
|
|
236
|
+
return fm_lines[:insert_at] + perm_block + fm_lines[insert_at:]
|
|
237
|
+
else:
|
|
238
|
+
return fm_lines + perm_block
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def extract_platform_metadata(fm_lines: list[str], platform: str) -> list[str]:
|
|
242
|
+
platform_re = re.compile(r'^(\s+)' + re.escape(platform) + r'\s*:')
|
|
243
|
+
|
|
244
|
+
in_metadata = False
|
|
245
|
+
platform_indent: int | None = None
|
|
246
|
+
content_base_indent: int | None = None
|
|
247
|
+
result: list[str] = []
|
|
248
|
+
|
|
249
|
+
for line in fm_lines:
|
|
250
|
+
if not in_metadata:
|
|
251
|
+
if METADATA_RE.match(line):
|
|
252
|
+
in_metadata = True
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
if platform_indent is None:
|
|
256
|
+
if not line or not line[0].isspace():
|
|
257
|
+
break
|
|
258
|
+
m = platform_re.match(line)
|
|
259
|
+
if m:
|
|
260
|
+
platform_indent = len(m.group(1))
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
if not line or not line[0].isspace():
|
|
264
|
+
break
|
|
265
|
+
current_indent = len(line) - len(line.lstrip())
|
|
266
|
+
if current_indent <= platform_indent:
|
|
267
|
+
break
|
|
268
|
+
if content_base_indent is None:
|
|
269
|
+
content_base_indent = current_indent
|
|
270
|
+
result.append(line[content_base_indent:])
|
|
271
|
+
|
|
272
|
+
return result
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def inject_metadata_fields(fm_lines: list[str], meta_lines: list[str]) -> list[str]:
|
|
276
|
+
if not meta_lines:
|
|
277
|
+
return fm_lines
|
|
278
|
+
|
|
279
|
+
meta_fields = []
|
|
280
|
+
for line in meta_lines:
|
|
281
|
+
if line and not line[0].isspace():
|
|
282
|
+
m = re.match(r'^(\S+)\s*:', line)
|
|
283
|
+
if m:
|
|
284
|
+
meta_fields.append(m.group(1))
|
|
285
|
+
|
|
286
|
+
stripped = strip_fields(fm_lines, meta_fields)
|
|
287
|
+
base = stripped if stripped is not None else list(fm_lines)
|
|
288
|
+
return base + meta_lines
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def rewrite_file(path: Path, new_fm_lines: list[str], original_lines: list[str], fm_end: int) -> None:
|
|
292
|
+
body = original_lines[fm_end + 1:]
|
|
293
|
+
result = ["---"] + new_fm_lines + ["---"] + body
|
|
294
|
+
text = "\n".join(result)
|
|
295
|
+
if text and not text.endswith("\n"):
|
|
296
|
+
text += "\n"
|
|
297
|
+
path.write_text(text)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def sanitize_installed(agents_dir: Path, fields: list[str], platform: str | None = None) -> int:
|
|
301
|
+
if not agents_dir.is_dir():
|
|
302
|
+
return 0
|
|
303
|
+
|
|
304
|
+
changed = 0
|
|
305
|
+
for f in sorted(agents_dir.glob("*.md")):
|
|
306
|
+
lines = f.read_text().splitlines()
|
|
307
|
+
fm = find_frontmatter(lines)
|
|
308
|
+
if fm is None:
|
|
309
|
+
continue
|
|
310
|
+
_, fm_end = fm
|
|
311
|
+
fm_lines = lines[1:fm_end]
|
|
312
|
+
|
|
313
|
+
platform_meta = extract_platform_metadata(fm_lines, platform) if platform else []
|
|
314
|
+
|
|
315
|
+
stripped = strip_fields(fm_lines, fields)
|
|
316
|
+
new_fm = stripped if stripped is not None else list(fm_lines)
|
|
317
|
+
|
|
318
|
+
if platform_meta:
|
|
319
|
+
new_fm = inject_metadata_fields(new_fm, platform_meta)
|
|
320
|
+
|
|
321
|
+
if new_fm == fm_lines:
|
|
322
|
+
continue
|
|
323
|
+
|
|
324
|
+
rewrite_file(f, new_fm, lines, fm_end)
|
|
325
|
+
changed += 1
|
|
326
|
+
name = f.stem
|
|
327
|
+
removed = [k for k in fields if any(re.match(r'^' + re.escape(k) + r'\s*:', l) for l in fm_lines)]
|
|
328
|
+
injected = [m.group(1) for l in platform_meta if (m := re.match(r'^(\S+)\s*:', l))]
|
|
329
|
+
parts = []
|
|
330
|
+
if removed:
|
|
331
|
+
parts.append(f"removed {', '.join(removed)}")
|
|
332
|
+
if injected:
|
|
333
|
+
parts.append(f"injected {', '.join(injected)}")
|
|
334
|
+
print(f" [strip] {name} — {'; '.join(parts)}")
|
|
335
|
+
|
|
336
|
+
return changed
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def sanitize_source(packages_dir: Path) -> int:
|
|
340
|
+
if not packages_dir.is_dir():
|
|
341
|
+
return 0
|
|
342
|
+
|
|
343
|
+
changed = 0
|
|
344
|
+
for f in sorted(packages_dir.glob("*/.apm/agents/*.agent.md")):
|
|
345
|
+
lines = f.read_text().splitlines()
|
|
346
|
+
fm = find_frontmatter(lines)
|
|
347
|
+
if fm is None:
|
|
348
|
+
continue
|
|
349
|
+
_, fm_end = fm
|
|
350
|
+
fm_lines = lines[1:fm_end]
|
|
351
|
+
|
|
352
|
+
if not has_array_tools(fm_lines):
|
|
353
|
+
continue
|
|
354
|
+
|
|
355
|
+
new_fm = add_permission_block(fm_lines)
|
|
356
|
+
if new_fm is None:
|
|
357
|
+
continue
|
|
358
|
+
|
|
359
|
+
rewrite_file(f, new_fm, lines, fm_end)
|
|
360
|
+
changed += 1
|
|
361
|
+
name = f.stem
|
|
362
|
+
tools = [l.split(":")[0].strip() for l in new_fm if l.startswith(" ") and ":" in l and l.strip() != "permission:"]
|
|
363
|
+
print(f" [convert] {name} — added permission ({', '.join(tools)})")
|
|
364
|
+
|
|
365
|
+
return changed
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def run_apm(subcommand: str, args: list[str]) -> int:
|
|
369
|
+
apm_exe = shutil.which("apm")
|
|
370
|
+
if apm_exe is None:
|
|
371
|
+
print("error: 'apm' command not found in PATH", file=sys.stderr)
|
|
372
|
+
return 1
|
|
373
|
+
|
|
374
|
+
cmd = [apm_exe, subcommand] + args
|
|
375
|
+
print(f"$ {' '.join(cmd)}")
|
|
376
|
+
result = subprocess.run(cmd)
|
|
377
|
+
return result.returncode
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def do_sanitize(platform_name: str | None, global_: bool, source: bool) -> None:
|
|
381
|
+
if platform_name is None:
|
|
382
|
+
for name in TARGET_STRIP_FIELDS:
|
|
383
|
+
print(f"[{name}]")
|
|
384
|
+
do_sanitize(name, global_, source)
|
|
385
|
+
return
|
|
386
|
+
|
|
387
|
+
fields = TARGET_STRIP_FIELDS.get(platform_name, [])
|
|
388
|
+
packages_dir = Path.cwd() / "packages"
|
|
389
|
+
|
|
390
|
+
changed_installed = 0
|
|
391
|
+
|
|
392
|
+
if global_:
|
|
393
|
+
dirs = get_global_agents_dirs(platform_name)
|
|
394
|
+
for d in dirs:
|
|
395
|
+
cnt = sanitize_installed(d, fields, platform_name)
|
|
396
|
+
if cnt:
|
|
397
|
+
print(f" ({d})")
|
|
398
|
+
changed_installed += cnt
|
|
399
|
+
else:
|
|
400
|
+
target_subdir = TARGET_DIRS.get(platform_name, f".{platform_name}")
|
|
401
|
+
target_dir = Path.cwd() / target_subdir
|
|
402
|
+
changed_installed = sanitize_installed(target_dir / "agents", fields, platform_name)
|
|
403
|
+
|
|
404
|
+
changed_source = 0
|
|
405
|
+
if source:
|
|
406
|
+
changed_source = sanitize_source(packages_dir)
|
|
407
|
+
|
|
408
|
+
parts = [f"{changed_installed} {'global ' if global_ else ''}installed files sanitized"]
|
|
409
|
+
if source:
|
|
410
|
+
parts.append(f"{changed_source} source files updated")
|
|
411
|
+
print(f"\nDone: {', '.join(parts)}")
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def main() -> None:
|
|
415
|
+
parser = build_parser()
|
|
416
|
+
ns, apm_args = parser.parse_known_args(sys.argv[1:])
|
|
417
|
+
|
|
418
|
+
if ns.command == "sanitize":
|
|
419
|
+
do_sanitize(ns.platform, ns.global_, ns.source)
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
apm_extra: list[str] = []
|
|
423
|
+
if ns.global_:
|
|
424
|
+
apm_extra.append("-g")
|
|
425
|
+
|
|
426
|
+
if ns.command == "install":
|
|
427
|
+
rc = run_apm("install", apm_extra + apm_args)
|
|
428
|
+
if rc != 0:
|
|
429
|
+
sys.exit(rc)
|
|
430
|
+
do_sanitize(ns.platform, ns.global_, ns.source)
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
if ns.command == "update":
|
|
434
|
+
if ns.yes:
|
|
435
|
+
apm_extra.insert(0, "--yes")
|
|
436
|
+
if ns.dry_run:
|
|
437
|
+
apm_extra.insert(0, "--dry-run")
|
|
438
|
+
rc = run_apm("update", apm_extra + apm_args)
|
|
439
|
+
if rc != 0 or ns.dry_run:
|
|
440
|
+
if rc != 0:
|
|
441
|
+
sys.exit(rc)
|
|
442
|
+
return
|
|
443
|
+
do_sanitize(ns.platform, ns.global_, ns.source)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"opencode": ["tools", "user-invocable", "agents", "metadata"],
|
|
3
|
+
"copilot": ["mode", "steps", "color", "permission", "task", "metadata"],
|
|
4
|
+
"claude": ["mode", "steps", "color", "tools", "permission", "user-invocable", "agents", "model", "metadata"],
|
|
5
|
+
"cursor": ["mode", "steps", "color", "permission", "metadata"],
|
|
6
|
+
"codex": ["mode", "steps", "color", "permission", "metadata"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
apmw/__init__.py,sha256=BkZpII5WVeyPMJ68A8Of2VcXtPCal6K_pEgu-4TJN50,13877
|
|
2
|
+
apmw/data/agent-strip-fields.json,sha256=OzumPaVWN-spMm0WZSqA6fZew_S_Elg6vx1fHT289X4,386
|
|
3
|
+
apmw-0.1.0.dist-info/METADATA,sha256=FnSCxc5qNKyZXw-Sq1GwkXkzcKN5A4lpFWUeo48cQqU,163
|
|
4
|
+
apmw-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
5
|
+
apmw-0.1.0.dist-info/entry_points.txt,sha256=NpuUklSa4JdJTN7uW6mc9CRix2osVPH2nkxICaVQg7w,35
|
|
6
|
+
apmw-0.1.0.dist-info/RECORD,,
|