proxy2model 2.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.
Files changed (66) hide show
  1. proxy2model/__init__.py +19 -0
  2. proxy2model/__main__.py +6 -0
  3. proxy2model/atomic.py +68 -0
  4. proxy2model/cli/__init__.py +3 -0
  5. proxy2model/cli/analyze.py +501 -0
  6. proxy2model/cli/app.py +408 -0
  7. proxy2model/cli/browse.py +279 -0
  8. proxy2model/cli/build.py +399 -0
  9. proxy2model/cli/common.py +190 -0
  10. proxy2model/cli/history.py +224 -0
  11. proxy2model/cli/mcp.py +184 -0
  12. proxy2model/cli/ui.py +62 -0
  13. proxy2model/core/__init__.py +1 -0
  14. proxy2model/core/changes.py +124 -0
  15. proxy2model/core/context.py +571 -0
  16. proxy2model/core/diff.py +330 -0
  17. proxy2model/core/domains.py +98 -0
  18. proxy2model/core/graph.py +562 -0
  19. proxy2model/core/methodology.py +343 -0
  20. proxy2model/core/model.py +998 -0
  21. proxy2model/core/stack.py +218 -0
  22. proxy2model/data/fonts/LICENSES.md +16 -0
  23. proxy2model/data/fonts/inter-latin-wght-normal.woff2 +0 -0
  24. proxy2model/data/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  25. proxy2model/data/fonts/nippo-200.woff2 +0 -0
  26. proxy2model/data/fonts/nippo-300.woff2 +0 -0
  27. proxy2model/data/fonts/nippo-400.woff2 +0 -0
  28. proxy2model/data/fonts/nippo-500.woff2 +0 -0
  29. proxy2model/data/fonts/nippo-700.woff2 +0 -0
  30. proxy2model/data/public_suffix_list.dat +16497 -0
  31. proxy2model/db/__init__.py +1 -0
  32. proxy2model/db/bql.py +710 -0
  33. proxy2model/db/bql_js.py +278 -0
  34. proxy2model/db/store.py +547 -0
  35. proxy2model/export/__init__.py +1 -0
  36. proxy2model/export/openapi.py +248 -0
  37. proxy2model/ingest/__init__.py +1 -0
  38. proxy2model/ingest/browse.py +181 -0
  39. proxy2model/ingest/browser.py +1058 -0
  40. proxy2model/ingest/crawl.py +673 -0
  41. proxy2model/ingest/extract.py +208 -0
  42. proxy2model/ingest/infer.py +75 -0
  43. proxy2model/ingest/journey.py +148 -0
  44. proxy2model/ingest/live.py +96 -0
  45. proxy2model/ingest/parse.py +1275 -0
  46. proxy2model/ingest/redact.py +535 -0
  47. proxy2model/py.typed +0 -0
  48. proxy2model/recon/__init__.py +1 -0
  49. proxy2model/recon/osint.py +607 -0
  50. proxy2model/report/__init__.py +1 -0
  51. proxy2model/report/animate.py +163 -0
  52. proxy2model/report/dash.py +469 -0
  53. proxy2model/report/fonts.py +51 -0
  54. proxy2model/report/html.py +988 -0
  55. proxy2model/report/inventory.py +258 -0
  56. proxy2model/report/map.py +515 -0
  57. proxy2model/report/pages.py +537 -0
  58. proxy2model/report/query.py +306 -0
  59. proxy2model/report/shot.py +55 -0
  60. proxy2model/report/view.py +213 -0
  61. proxy2model-2.0.0.dist-info/METADATA +148 -0
  62. proxy2model-2.0.0.dist-info/RECORD +66 -0
  63. proxy2model-2.0.0.dist-info/WHEEL +5 -0
  64. proxy2model-2.0.0.dist-info/entry_points.txt +2 -0
  65. proxy2model-2.0.0.dist-info/licenses/LICENSE +201 -0
  66. proxy2model-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ """
2
+ The viral surface: turn a build into an animation people share.
3
+
4
+ Two outputs, no runtime dependencies:
5
+
6
+ render_terminal(model) -> prints a typed, colored build sequence to the
7
+ terminal (the `--animate` flag). Looks like a
8
+ live demo even when it's deterministic.
9
+
10
+ render_svg(model) -> a single self-contained animated SVG string that
11
+ loops in a GitHub README with no JS and no CDN.
12
+ This is the thing that gets screenshotted.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import sys
18
+ import time
19
+
20
+ from ..atomic import atomic_write
21
+ from ..core.model import Model
22
+
23
+ # brand
24
+ ORANGE = "\033[38;5;208m"
25
+ CYAN = "\033[38;5;39m"
26
+ VIOLET = "\033[38;5;99m"
27
+ DIM = "\033[38;5;244m"
28
+ WHITE = "\033[97m"
29
+ BOLD = "\033[1m"
30
+ RESET = "\033[0m"
31
+
32
+
33
+ def _type(text: str, delay: float = 0.012, color: str = WHITE, nl: bool = True):
34
+ for ch in text:
35
+ sys.stdout.write(color + ch + RESET)
36
+ sys.stdout.flush()
37
+ time.sleep(delay)
38
+ if nl:
39
+ sys.stdout.write("\n")
40
+
41
+
42
+ def render_terminal(m: Model, fast: bool = False) -> None:
43
+ d = 0.0 if fast else 0.012
44
+ c = m.counts()
45
+ print()
46
+ _type("$ proxy2model history.xml --webapp " + m.name, d, ORANGE)
47
+ time.sleep(0.15 if not fast else 0)
48
+ for stage, note in [
49
+ ("parsing the capture", "streaming exchanges, decoding"),
50
+ ("redacting values", "tokens, cookies, keys, PII → fingerprints"),
51
+ ("typing assets", "routes · scripts · endpoints · params"),
52
+ ("relating nodes", "OBSERVED vs INFERRED edges"),
53
+ ("reconciling", "code references vs runtime traffic"),
54
+ ("naming unknowns", "what the capture could not answer"),
55
+ ]:
56
+ _type(f" {DIM}▸{RESET} {stage:<20}{DIM}{note}{RESET}", d, WHITE, nl=True)
57
+ time.sleep(0.08 if not fast else 0)
58
+ print()
59
+ # the payoff line
60
+ static = c["api_state"].get("STATIC_ONLY", 0)
61
+ both = c["api_state"].get("BOTH", 0)
62
+ runtime = c["api_state"].get("RUNTIME_ONLY", 0)
63
+ _type(f"{BOLD} routes {c['routes']} · APIs {c['endpoints']} "
64
+ f"({both} both, {runtime} runtime-only, {static} static-only)"
65
+ f" · edges {c['edges']} · nodes {c['nodes']}{RESET}", d, CYAN)
66
+ _type(f" {VIOLET}{c['unknowns']} named unknowns{RESET} "
67
+ f"{DIM}absence = a gap in observation, never in the target{RESET}", d, WHITE)
68
+ print()
69
+ _type(" a model your AI can reason over — not a dump it hallucinates on", d, ORANGE)
70
+ print()
71
+
72
+
73
+ # ---------------------------------------------------------------- SVG --------
74
+
75
+ def render_svg(m: Model) -> str:
76
+ c = m.counts()
77
+ both = c["api_state"].get("BOTH", 0)
78
+ runtime = c["api_state"].get("RUNTIME_ONLY", 0)
79
+ static = c["api_state"].get("STATIC_ONLY", 0)
80
+
81
+ # the six build stages, revealed one at a time
82
+ stages = [
83
+ ("parse", "streaming the capture"),
84
+ ("redact", "values masked before any write"),
85
+ ("type", "routes · scripts · endpoints"),
86
+ ("relate", "observed vs inferred edges"),
87
+ ("reconcile", "code vs runtime"),
88
+ ("model", "an evidence-backed graph"),
89
+ ]
90
+ W, H = 760, 480
91
+ line_h = 30
92
+ base_y = 150
93
+ dur_each = 0.9 # seconds per line reveal
94
+
95
+ parts = [
96
+ f'<svg viewBox="0 0 {W} {H}" xmlns="http://www.w3.org/2000/svg" '
97
+ f'font-family="ui-monospace,SFMono-Regular,Menlo,monospace" role="img" '
98
+ f'aria-label="proxy2model turning captured traffic into a web-app model">',
99
+ f'<rect width="{W}" height="{H}" rx="14" fill="#0d1117"/>',
100
+ # window chrome
101
+ '<circle cx="24" cy="26" r="6" fill="#ff5f56"/>',
102
+ '<circle cx="46" cy="26" r="6" fill="#ffbd2e"/>',
103
+ '<circle cx="68" cy="26" r="6" fill="#27c93f"/>',
104
+ f'<text x="{W/2}" y="30" fill="#8b98a8" font-size="12" text-anchor="middle">proxy2model</text>',
105
+ # prompt line (typed via width mask)
106
+ '<text x="28" y="78" fill="#e85002" font-size="15" font-weight="700">'
107
+ '$ proxy2model history.xml</text>',
108
+ '<rect x="28" y="66" width="0" height="18" fill="#0d1117">'
109
+ '<animate attributeName="width" from="360" to="0" dur="1s" fill="freeze"/></rect>',
110
+ '<text x="392" y="78" fill="#3a424e" font-size="15">'
111
+ '<animate attributeName="opacity" values="1;0;1" dur="0.8s" repeatCount="indefinite"/>▋</text>',
112
+ ]
113
+
114
+ # stage lines, each fades in in sequence
115
+ for i, (stage, note) in enumerate(stages):
116
+ y = base_y + i * line_h
117
+ begin = 1.0 + i * dur_each
118
+ parts.append(
119
+ f'<g opacity="0"><animate attributeName="opacity" from="0" to="1" '
120
+ f'dur="0.35s" begin="{begin:.2f}s" fill="freeze"/>'
121
+ f'<text x="40" y="{y}" fill="#4fd1c5" font-size="14">▸</text>'
122
+ f'<text x="66" y="{y}" fill="#e6edf3" font-size="14">{stage}</text>'
123
+ f'<text x="180" y="{y}" fill="#8b98a8" font-size="13">{note}</text></g>'
124
+ )
125
+
126
+ reveal_all = 1.0 + len(stages) * dur_each + 0.3
127
+ # payoff panel
128
+ py = base_y + len(stages) * line_h + 26
129
+ parts.append(
130
+ f'<g opacity="0"><animate attributeName="opacity" from="0" to="1" '
131
+ f'dur="0.5s" begin="{reveal_all:.2f}s" fill="freeze"/>'
132
+ f'<rect x="28" y="{py-22}" width="{W-56}" height="96" rx="10" '
133
+ f'fill="#11161d" stroke="#1e2733"/>'
134
+ f'<text x="44" y="{py+2}" fill="#e6edf3" font-size="15" font-weight="700">'
135
+ f'routes {c["routes"]} · APIs {c["endpoints"]} · edges {c["edges"]}</text>'
136
+ f'<text x="44" y="{py+26}" fill="#39d0ff" font-size="13">'
137
+ f'{both} both · {runtime} runtime-only · </text>'
138
+ f'<text x="{44 + 8.0*len(str(both)+str(runtime)) + 165}" y="{py+26}" '
139
+ f'fill="#e85002" font-size="13" font-weight="700">{static} static-only ← look here</text>'
140
+ f'<text x="44" y="{py+50}" fill="#9b8cff" font-size="13">'
141
+ f'{c["unknowns"]} named unknowns — what the model does not know</text></g>'
142
+ )
143
+ # closing tagline
144
+ parts.append(
145
+ f'<text x="{W/2}" y="{H-24}" fill="#8b98a8" font-size="13" text-anchor="middle" '
146
+ f'opacity="0"><animate attributeName="opacity" from="0" to="1" dur="0.6s" '
147
+ f'begin="{reveal_all+0.6:.2f}s" fill="freeze"/>'
148
+ f'a model your AI reasons over — not a dump it hallucinates on</text>'
149
+ )
150
+ # loop: fade whole thing at the end and restart
151
+ total = reveal_all + 3.0
152
+ parts.append(
153
+ f'<rect width="{W}" height="{H}" fill="#0d1117" opacity="0">'
154
+ f'<animate attributeName="opacity" values="0;0;1;0" '
155
+ f'keyTimes="0;0.93;0.97;1" dur="{total:.1f}s" repeatCount="indefinite"/></rect>'
156
+ )
157
+ parts.append("</svg>")
158
+ return "\n".join(parts)
159
+
160
+
161
+ def write_svg(m: Model, path: str) -> None:
162
+ with atomic_write(path) as f:
163
+ f.write(render_svg(m))