rpp-protocol 0.1.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.
- rpp/__init__.py +60 -0
- rpp/adapters/__init__.py +14 -0
- rpp/adapters/filesystem.py +151 -0
- rpp/adapters/memory.py +95 -0
- rpp/address.py +249 -0
- rpp/cli.py +608 -0
- rpp/i18n.py +426 -0
- rpp/resolver.py +216 -0
- rpp/visual.py +389 -0
- rpp_protocol-0.1.5.dist-info/METADATA +390 -0
- rpp_protocol-0.1.5.dist-info/RECORD +14 -0
- rpp_protocol-0.1.5.dist-info/WHEEL +4 -0
- rpp_protocol-0.1.5.dist-info/entry_points.txt +2 -0
- rpp_protocol-0.1.5.dist-info/licenses/LICENSE +219 -0
rpp/cli.py
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RPP Command Line Interface
|
|
3
|
+
|
|
4
|
+
Emulator-proof CLI for RPP operations.
|
|
5
|
+
Works via stdin/stdout with no ANSI codes or cursor control.
|
|
6
|
+
Compatible with: SSH, PuTTY, serial terminals, air-gapped systems.
|
|
7
|
+
|
|
8
|
+
Commands:
|
|
9
|
+
rpp encode --theta T --phi P --shell S --harmonic H
|
|
10
|
+
rpp decode --address 0xADDRESS
|
|
11
|
+
rpp resolve --address 0xADDRESS [--operation read|write]
|
|
12
|
+
|
|
13
|
+
Flags:
|
|
14
|
+
--fancy, -f: Enable ANSI colors for modern terminals
|
|
15
|
+
--visual, -V: Show ASCII diagrams and visual feedback
|
|
16
|
+
|
|
17
|
+
Exit codes:
|
|
18
|
+
0: Success
|
|
19
|
+
1: Invalid input
|
|
20
|
+
2: Resolution denied
|
|
21
|
+
3: Internal error
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import sys
|
|
25
|
+
import json
|
|
26
|
+
import argparse
|
|
27
|
+
import io
|
|
28
|
+
from typing import List, Optional, TextIO
|
|
29
|
+
|
|
30
|
+
# Ensure UTF-8 output on Windows
|
|
31
|
+
if sys.platform == "win32":
|
|
32
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
|
33
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
|
34
|
+
|
|
35
|
+
from rpp.address import (
|
|
36
|
+
from_raw,
|
|
37
|
+
from_components,
|
|
38
|
+
parse_address,
|
|
39
|
+
MAX_SHELL,
|
|
40
|
+
MAX_THETA,
|
|
41
|
+
MAX_PHI,
|
|
42
|
+
MAX_HARMONIC,
|
|
43
|
+
)
|
|
44
|
+
from rpp.resolver import resolve
|
|
45
|
+
from rpp import visual
|
|
46
|
+
from rpp.i18n import t, get_supported_languages, DEFAULT_LANG
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Exit codes
|
|
50
|
+
EXIT_SUCCESS = 0
|
|
51
|
+
EXIT_INVALID_INPUT = 1
|
|
52
|
+
EXIT_DENIED = 2
|
|
53
|
+
EXIT_ERROR = 3
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def output(text: str, file: TextIO = sys.stdout) -> None:
|
|
57
|
+
"""Write output line. No ANSI, no color, just plain text."""
|
|
58
|
+
print(text, file=file, flush=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def output_json(data: dict, file: TextIO = sys.stdout) -> None:
|
|
62
|
+
"""Write JSON output. Compact, single-line for simple parsing."""
|
|
63
|
+
print(json.dumps(data, separators=(",", ":")), file=file, flush=True)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def error(text: str) -> None:
|
|
67
|
+
"""Write error message to stderr."""
|
|
68
|
+
print(f"error: {text}", file=sys.stderr, flush=True)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def cmd_encode(args: argparse.Namespace) -> int:
|
|
72
|
+
"""Handle encode command."""
|
|
73
|
+
fancy = getattr(args, 'fancy', False)
|
|
74
|
+
show_visual = getattr(args, 'visual', False)
|
|
75
|
+
lang = getattr(args, 'lang', DEFAULT_LANG)
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Validate ranges
|
|
79
|
+
if not (0 <= args.shell <= MAX_SHELL):
|
|
80
|
+
error(f"shell must be 0-{MAX_SHELL}")
|
|
81
|
+
return EXIT_INVALID_INPUT
|
|
82
|
+
if not (0 <= args.theta <= MAX_THETA):
|
|
83
|
+
error(f"theta must be 0-{MAX_THETA}")
|
|
84
|
+
return EXIT_INVALID_INPUT
|
|
85
|
+
if not (0 <= args.phi <= MAX_PHI):
|
|
86
|
+
error(f"phi must be 0-{MAX_PHI}")
|
|
87
|
+
return EXIT_INVALID_INPUT
|
|
88
|
+
if not (0 <= args.harmonic <= MAX_HARMONIC):
|
|
89
|
+
error(f"harmonic must be 0-{MAX_HARMONIC}")
|
|
90
|
+
return EXIT_INVALID_INPUT
|
|
91
|
+
|
|
92
|
+
# Encode
|
|
93
|
+
addr = from_components(args.shell, args.theta, args.phi, args.harmonic)
|
|
94
|
+
|
|
95
|
+
if args.json:
|
|
96
|
+
output_json(addr.to_dict())
|
|
97
|
+
else:
|
|
98
|
+
# Get translated names
|
|
99
|
+
shell_name = t(f"shell_{addr.shell_name.lower()}", lang)
|
|
100
|
+
sector_name = t(f"sector_{addr.sector_name.lower()}", lang)
|
|
101
|
+
grounding = t(f"grounding_{addr.grounding_level.lower()}", lang)
|
|
102
|
+
|
|
103
|
+
# Show operation status
|
|
104
|
+
output(visual.operation_status(t("encode", lang), True, fancy))
|
|
105
|
+
output("")
|
|
106
|
+
|
|
107
|
+
if show_visual:
|
|
108
|
+
# Show full bit layout diagram
|
|
109
|
+
output(visual.bit_layout_diagram(
|
|
110
|
+
addr.shell, addr.theta, addr.phi, addr.harmonic,
|
|
111
|
+
addr.raw, fancy
|
|
112
|
+
))
|
|
113
|
+
output("")
|
|
114
|
+
# Show consent meter
|
|
115
|
+
output(visual.consent_meter(addr.phi, fancy))
|
|
116
|
+
output("")
|
|
117
|
+
# Show shell tier
|
|
118
|
+
output(f"{t('shell', lang).title()}:")
|
|
119
|
+
output(visual.shell_tier_visual(addr.shell, fancy))
|
|
120
|
+
else:
|
|
121
|
+
# Compact output with mini visual
|
|
122
|
+
output(visual.address_mini(
|
|
123
|
+
addr.to_hex(), shell_name,
|
|
124
|
+
sector_name, grounding, fancy
|
|
125
|
+
))
|
|
126
|
+
output("")
|
|
127
|
+
output(f" {t('shell', lang)}: {addr.shell} ({shell_name})")
|
|
128
|
+
output(f" {t('theta', lang)}: {addr.theta} ({sector_name})")
|
|
129
|
+
output(f" {t('phi', lang)}: {addr.phi} ({grounding})")
|
|
130
|
+
output(f" {t('harmonic', lang)}: {addr.harmonic}")
|
|
131
|
+
|
|
132
|
+
return EXIT_SUCCESS
|
|
133
|
+
|
|
134
|
+
except ValueError as e:
|
|
135
|
+
error(str(e))
|
|
136
|
+
return EXIT_INVALID_INPUT
|
|
137
|
+
except Exception as e:
|
|
138
|
+
error(f"internal error: {e}")
|
|
139
|
+
return EXIT_ERROR
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cmd_decode(args: argparse.Namespace) -> int:
|
|
143
|
+
"""Handle decode command."""
|
|
144
|
+
fancy = getattr(args, 'fancy', False)
|
|
145
|
+
show_visual = getattr(args, 'visual', False)
|
|
146
|
+
lang = getattr(args, 'lang', DEFAULT_LANG)
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
# Parse address (handles hex or decimal)
|
|
150
|
+
raw = parse_address(args.address)
|
|
151
|
+
addr = from_raw(raw)
|
|
152
|
+
|
|
153
|
+
if args.json:
|
|
154
|
+
output_json(addr.to_dict())
|
|
155
|
+
else:
|
|
156
|
+
# Get translated names
|
|
157
|
+
shell_name = t(f"shell_{addr.shell_name.lower()}", lang)
|
|
158
|
+
sector_name = t(f"sector_{addr.sector_name.lower()}", lang)
|
|
159
|
+
grounding = t(f"grounding_{addr.grounding_level.lower()}", lang)
|
|
160
|
+
|
|
161
|
+
# Show operation status
|
|
162
|
+
output(visual.operation_status(t("decode", lang), True, fancy))
|
|
163
|
+
output("")
|
|
164
|
+
|
|
165
|
+
if show_visual:
|
|
166
|
+
# Show full bit layout diagram
|
|
167
|
+
output(visual.bit_layout_diagram(
|
|
168
|
+
addr.shell, addr.theta, addr.phi, addr.harmonic,
|
|
169
|
+
addr.raw, fancy
|
|
170
|
+
))
|
|
171
|
+
output("")
|
|
172
|
+
# Show consent meter
|
|
173
|
+
output(visual.consent_meter(addr.phi, fancy))
|
|
174
|
+
output("")
|
|
175
|
+
# Show theta wheel
|
|
176
|
+
output(visual.theta_wheel(addr.theta, fancy))
|
|
177
|
+
else:
|
|
178
|
+
# Compact output with mini visual
|
|
179
|
+
output(visual.address_mini(
|
|
180
|
+
addr.to_hex(), shell_name,
|
|
181
|
+
sector_name, grounding, fancy
|
|
182
|
+
))
|
|
183
|
+
output("")
|
|
184
|
+
output(f" {t('shell', lang)}: {addr.shell} ({shell_name})")
|
|
185
|
+
output(f" {t('theta', lang)}: {addr.theta} ({sector_name})")
|
|
186
|
+
output(f" {t('phi', lang)}: {addr.phi} ({grounding})")
|
|
187
|
+
output(f" {t('harmonic', lang)}: {addr.harmonic}")
|
|
188
|
+
|
|
189
|
+
return EXIT_SUCCESS
|
|
190
|
+
|
|
191
|
+
except ValueError as e:
|
|
192
|
+
error(str(e))
|
|
193
|
+
return EXIT_INVALID_INPUT
|
|
194
|
+
except Exception as e:
|
|
195
|
+
error(f"internal error: {e}")
|
|
196
|
+
return EXIT_ERROR
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def cmd_resolve(args: argparse.Namespace) -> int:
|
|
200
|
+
"""Handle resolve command."""
|
|
201
|
+
fancy = getattr(args, 'fancy', False)
|
|
202
|
+
show_visual = getattr(args, 'visual', False)
|
|
203
|
+
lang = getattr(args, 'lang', DEFAULT_LANG)
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
# Parse address
|
|
207
|
+
raw = parse_address(args.address)
|
|
208
|
+
addr = from_raw(raw)
|
|
209
|
+
|
|
210
|
+
# Build context
|
|
211
|
+
context = {}
|
|
212
|
+
if args.consent:
|
|
213
|
+
context["consent"] = args.consent
|
|
214
|
+
if args.emergency:
|
|
215
|
+
context["emergency_override"] = True
|
|
216
|
+
|
|
217
|
+
# Resolve
|
|
218
|
+
result = resolve(raw, operation=args.operation, context=context)
|
|
219
|
+
|
|
220
|
+
if args.json:
|
|
221
|
+
output_json(result.to_dict())
|
|
222
|
+
else:
|
|
223
|
+
# Show operation status
|
|
224
|
+
output(visual.operation_status(t("resolve", lang), result.allowed, fancy))
|
|
225
|
+
output("")
|
|
226
|
+
|
|
227
|
+
if show_visual:
|
|
228
|
+
# Show routing diagram
|
|
229
|
+
output(visual.routing_diagram(
|
|
230
|
+
addr.shell, result.allowed, result.route,
|
|
231
|
+
result.reason, fancy
|
|
232
|
+
))
|
|
233
|
+
output("")
|
|
234
|
+
# Show consent meter for context
|
|
235
|
+
output(visual.consent_meter(addr.phi, fancy))
|
|
236
|
+
else:
|
|
237
|
+
# Compact output (ASCII-safe for Windows)
|
|
238
|
+
output(f" {t('allowed', lang)}: {str(result.allowed).lower()}")
|
|
239
|
+
output(f" {t('route', lang)}: {result.route if result.route else 'null'}")
|
|
240
|
+
output(f" {t('reason', lang)}: {result.reason}")
|
|
241
|
+
|
|
242
|
+
# Exit code based on result
|
|
243
|
+
if result.allowed:
|
|
244
|
+
return EXIT_SUCCESS
|
|
245
|
+
else:
|
|
246
|
+
return EXIT_DENIED
|
|
247
|
+
|
|
248
|
+
except ValueError as e:
|
|
249
|
+
error(str(e))
|
|
250
|
+
return EXIT_INVALID_INPUT
|
|
251
|
+
except Exception as e:
|
|
252
|
+
error(f"internal error: {e}")
|
|
253
|
+
return EXIT_ERROR
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def cmd_demo(args: argparse.Namespace) -> int:
|
|
257
|
+
"""Run demonstration of the three core scenarios."""
|
|
258
|
+
fancy = getattr(args, 'fancy', False)
|
|
259
|
+
lang = getattr(args, 'lang', DEFAULT_LANG)
|
|
260
|
+
|
|
261
|
+
# Show banner
|
|
262
|
+
output(visual.demo_banner(fancy))
|
|
263
|
+
output("")
|
|
264
|
+
|
|
265
|
+
# Scenario 1: Allowed read (low phi)
|
|
266
|
+
output("=" * 60)
|
|
267
|
+
output(f" {t('scenario_1_title', lang)}")
|
|
268
|
+
output("=" * 60)
|
|
269
|
+
output("")
|
|
270
|
+
addr1 = from_components(shell=0, theta=12, phi=40, harmonic=1)
|
|
271
|
+
output(visual.bit_layout_diagram(
|
|
272
|
+
addr1.shell, addr1.theta, addr1.phi, addr1.harmonic,
|
|
273
|
+
addr1.raw, fancy
|
|
274
|
+
))
|
|
275
|
+
output("")
|
|
276
|
+
result1 = resolve(addr1.raw, operation="read")
|
|
277
|
+
output(visual.routing_diagram(
|
|
278
|
+
addr1.shell, result1.allowed, result1.route,
|
|
279
|
+
result1.reason, fancy
|
|
280
|
+
))
|
|
281
|
+
output("")
|
|
282
|
+
output(visual.consent_meter(addr1.phi, fancy))
|
|
283
|
+
output("")
|
|
284
|
+
|
|
285
|
+
# Scenario 2: Denied write (high phi)
|
|
286
|
+
output("=" * 60)
|
|
287
|
+
output(f" {t('scenario_2_title', lang)}")
|
|
288
|
+
output("=" * 60)
|
|
289
|
+
output("")
|
|
290
|
+
addr2 = from_components(shell=0, theta=100, phi=450, harmonic=64)
|
|
291
|
+
output(visual.bit_layout_diagram(
|
|
292
|
+
addr2.shell, addr2.theta, addr2.phi, addr2.harmonic,
|
|
293
|
+
addr2.raw, fancy
|
|
294
|
+
))
|
|
295
|
+
output("")
|
|
296
|
+
result2 = resolve(addr2.raw, operation="write")
|
|
297
|
+
output(visual.routing_diagram(
|
|
298
|
+
addr2.shell, result2.allowed, result2.route,
|
|
299
|
+
result2.reason, fancy
|
|
300
|
+
))
|
|
301
|
+
output("")
|
|
302
|
+
output(visual.consent_meter(addr2.phi, fancy))
|
|
303
|
+
output("")
|
|
304
|
+
|
|
305
|
+
# Scenario 3: Routed to archive (cold shell)
|
|
306
|
+
output("=" * 60)
|
|
307
|
+
output(f" {t('scenario_3_title', lang)}")
|
|
308
|
+
output("=" * 60)
|
|
309
|
+
output("")
|
|
310
|
+
addr3 = from_components(shell=2, theta=200, phi=128, harmonic=32)
|
|
311
|
+
output(visual.bit_layout_diagram(
|
|
312
|
+
addr3.shell, addr3.theta, addr3.phi, addr3.harmonic,
|
|
313
|
+
addr3.raw, fancy
|
|
314
|
+
))
|
|
315
|
+
output("")
|
|
316
|
+
result3 = resolve(addr3.raw, operation="read")
|
|
317
|
+
output(visual.routing_diagram(
|
|
318
|
+
addr3.shell, result3.allowed, result3.route,
|
|
319
|
+
result3.reason, fancy
|
|
320
|
+
))
|
|
321
|
+
output("")
|
|
322
|
+
output("Shell Tiers:")
|
|
323
|
+
output(visual.shell_tier_visual(addr3.shell, fancy))
|
|
324
|
+
output("")
|
|
325
|
+
|
|
326
|
+
# Summary
|
|
327
|
+
output("=" * 60)
|
|
328
|
+
output(visual.success_box(t("demonstration_complete", lang), fancy))
|
|
329
|
+
output("")
|
|
330
|
+
output("Key takeaways:")
|
|
331
|
+
output(f" * {t('takeaway_grounded', lang)}")
|
|
332
|
+
output(f" * {t('takeaway_ethereal', lang)}")
|
|
333
|
+
output(f" * {t('takeaway_cold', lang)}")
|
|
334
|
+
output("")
|
|
335
|
+
|
|
336
|
+
return EXIT_SUCCESS
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def cmd_version(args: argparse.Namespace) -> int:
|
|
340
|
+
"""Show version."""
|
|
341
|
+
from rpp import __version__
|
|
342
|
+
output(f"rpp {__version__}")
|
|
343
|
+
return EXIT_SUCCESS
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def cmd_tutorial(args: argparse.Namespace) -> int:
|
|
347
|
+
"""Run interactive tutorial explaining RPP concepts."""
|
|
348
|
+
fancy = getattr(args, 'fancy', False)
|
|
349
|
+
lang = getattr(args, 'lang', DEFAULT_LANG)
|
|
350
|
+
|
|
351
|
+
output(visual.demo_banner(fancy))
|
|
352
|
+
output("")
|
|
353
|
+
output("=" * 60)
|
|
354
|
+
output(f" {t('tutorial_welcome', lang)}")
|
|
355
|
+
output("=" * 60)
|
|
356
|
+
output("")
|
|
357
|
+
|
|
358
|
+
# Section 1: What is RPP?
|
|
359
|
+
output("-" * 60)
|
|
360
|
+
output(f" SECTION 1: {t('tutorial_what_is', lang)}")
|
|
361
|
+
output("-" * 60)
|
|
362
|
+
output("")
|
|
363
|
+
output("RPP (Rotational Packet Protocol) encodes MEANING directly")
|
|
364
|
+
output("into addresses. Instead of opaque memory locations, every")
|
|
365
|
+
output("28-bit address carries semantic information:")
|
|
366
|
+
output("")
|
|
367
|
+
output(" * WHERE data lives (Shell: storage tier)")
|
|
368
|
+
output(" * WHAT type it is (Theta: semantic sector)")
|
|
369
|
+
output(" * WHO can access it (Phi: consent/grounding level)")
|
|
370
|
+
output(" * HOW it behaves (Harmonic: frequency/mode)")
|
|
371
|
+
output("")
|
|
372
|
+
|
|
373
|
+
# Section 2: The 28-bit Structure
|
|
374
|
+
output("-" * 60)
|
|
375
|
+
output(f" SECTION 2: {t('tutorial_address', lang)}")
|
|
376
|
+
output("-" * 60)
|
|
377
|
+
output("")
|
|
378
|
+
output("Every RPP address is exactly 28 bits:")
|
|
379
|
+
output("")
|
|
380
|
+
output(" +--------+-------+---------+---------+----------+")
|
|
381
|
+
output(" | Reserved| Shell | Theta | Phi | Harmonic |")
|
|
382
|
+
output(" | 4 bits | 2 bits| 9 bits | 9 bits | 8 bits |")
|
|
383
|
+
output(" +--------+-------+---------+---------+----------+")
|
|
384
|
+
output("")
|
|
385
|
+
|
|
386
|
+
# Show example
|
|
387
|
+
example_addr = from_components(shell=1, theta=150, phi=200, harmonic=42)
|
|
388
|
+
output("Example: Encoding shell=1, theta=150, phi=200, harmonic=42")
|
|
389
|
+
output("")
|
|
390
|
+
output(visual.bit_layout_diagram(
|
|
391
|
+
example_addr.shell, example_addr.theta, example_addr.phi,
|
|
392
|
+
example_addr.harmonic, example_addr.raw, fancy
|
|
393
|
+
))
|
|
394
|
+
output("")
|
|
395
|
+
|
|
396
|
+
# Section 3: Shell Tiers
|
|
397
|
+
output("-" * 60)
|
|
398
|
+
output(" SECTION 3: Shell Tiers (Storage Routing)")
|
|
399
|
+
output("-" * 60)
|
|
400
|
+
output("")
|
|
401
|
+
output("The SHELL (2 bits) determines storage tier routing:")
|
|
402
|
+
output("")
|
|
403
|
+
output(" Shell 0 (Hot): In-memory, immediate access")
|
|
404
|
+
output(" Shell 1 (Warm): Fast storage, quick retrieval")
|
|
405
|
+
output(" Shell 2 (Cold): Archive storage, slower access")
|
|
406
|
+
output(" Shell 3 (Frozen): Deep archive, explicit consent required")
|
|
407
|
+
output("")
|
|
408
|
+
output(visual.shell_tier_visual(1, fancy))
|
|
409
|
+
output("")
|
|
410
|
+
|
|
411
|
+
# Section 4: Theta Sectors
|
|
412
|
+
output("-" * 60)
|
|
413
|
+
output(" SECTION 4: Theta Sectors (Semantic Classification)")
|
|
414
|
+
output("-" * 60)
|
|
415
|
+
output("")
|
|
416
|
+
output("The THETA (9 bits, 0-511) classifies data semantically:")
|
|
417
|
+
output("")
|
|
418
|
+
output(" 0-63: Gene - Core identity, genetic data")
|
|
419
|
+
output(" 64-127: Memory - Experiential records")
|
|
420
|
+
output(" 128-191: Witness - Observational data")
|
|
421
|
+
output(" 192-255: Dream - Aspirational/creative content")
|
|
422
|
+
output(" 256-319: Bridge - Connective/relational data")
|
|
423
|
+
output(" 320-383: Guardian - Protective/security data")
|
|
424
|
+
output(" 384-447: Emergence - Evolving/transforming content")
|
|
425
|
+
output(" 448-511: Meta - Self-referential/meta data")
|
|
426
|
+
output("")
|
|
427
|
+
output(visual.theta_wheel(150, fancy))
|
|
428
|
+
output("")
|
|
429
|
+
|
|
430
|
+
# Section 5: Phi Grounding
|
|
431
|
+
output("-" * 60)
|
|
432
|
+
output(" SECTION 5: Phi Grounding (Consent Levels)")
|
|
433
|
+
output("-" * 60)
|
|
434
|
+
output("")
|
|
435
|
+
output("The PHI (9 bits, 0-511) determines consent requirements:")
|
|
436
|
+
output("")
|
|
437
|
+
output(" 0-127: Grounded - Open access, no consent needed")
|
|
438
|
+
output(" 128-255: Transitional - Basic consent for modifications")
|
|
439
|
+
output(" 256-383: Abstract - Elevated consent required")
|
|
440
|
+
output(" 384-511: Ethereal - Explicit consent for all ops")
|
|
441
|
+
output("")
|
|
442
|
+
output("Low phi (grounded):")
|
|
443
|
+
output(visual.consent_meter(50, fancy))
|
|
444
|
+
output("")
|
|
445
|
+
output("High phi (ethereal):")
|
|
446
|
+
output(visual.consent_meter(450, fancy))
|
|
447
|
+
output("")
|
|
448
|
+
|
|
449
|
+
# Section 6: Resolution
|
|
450
|
+
output("-" * 60)
|
|
451
|
+
output(f" SECTION 6: {t('tutorial_resolver', lang)}")
|
|
452
|
+
output("-" * 60)
|
|
453
|
+
output("")
|
|
454
|
+
output("When you access an address, the RESOLVER checks:")
|
|
455
|
+
output("")
|
|
456
|
+
output(" 1. Is the operation (read/write/delete) allowed?")
|
|
457
|
+
output(" 2. Does the phi level require consent?")
|
|
458
|
+
output(" 3. Which storage backend should handle it?")
|
|
459
|
+
output("")
|
|
460
|
+
|
|
461
|
+
# Show allowed vs denied
|
|
462
|
+
allowed_addr = from_components(shell=0, theta=12, phi=40, harmonic=1)
|
|
463
|
+
result_ok = resolve(allowed_addr.raw, operation="read")
|
|
464
|
+
output("Example: Reading from grounded zone (phi=40)")
|
|
465
|
+
output(visual.routing_diagram(
|
|
466
|
+
allowed_addr.shell, result_ok.allowed, result_ok.route,
|
|
467
|
+
result_ok.reason, fancy
|
|
468
|
+
))
|
|
469
|
+
output("")
|
|
470
|
+
|
|
471
|
+
denied_addr = from_components(shell=0, theta=100, phi=450, harmonic=64)
|
|
472
|
+
result_denied = resolve(denied_addr.raw, operation="write")
|
|
473
|
+
output("Example: Writing to ethereal zone (phi=450)")
|
|
474
|
+
output(visual.routing_diagram(
|
|
475
|
+
denied_addr.shell, result_denied.allowed, result_denied.route,
|
|
476
|
+
result_denied.reason, fancy
|
|
477
|
+
))
|
|
478
|
+
output("")
|
|
479
|
+
|
|
480
|
+
# Summary
|
|
481
|
+
output("=" * 60)
|
|
482
|
+
output(visual.success_box(t("demonstration_complete", lang), fancy))
|
|
483
|
+
output("")
|
|
484
|
+
output(f"{t('tutorial_try_it', lang)}:")
|
|
485
|
+
output(" rpp encode --shell 0 --theta 12 --phi 40 --harmonic 1")
|
|
486
|
+
output(" rpp decode --address 0x0182801")
|
|
487
|
+
output(" rpp resolve --address 0x0182801 --operation read")
|
|
488
|
+
output(" rpp demo")
|
|
489
|
+
output("")
|
|
490
|
+
output("Add --visual for detailed diagrams, --fancy for colors!")
|
|
491
|
+
output("")
|
|
492
|
+
|
|
493
|
+
return EXIT_SUCCESS
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
497
|
+
"""Build the argument parser."""
|
|
498
|
+
parser = argparse.ArgumentParser(
|
|
499
|
+
prog="rpp",
|
|
500
|
+
description="RPP - Rotational Packet Protocol CLI",
|
|
501
|
+
epilog="See https://github.com/anywave/rpp-spec for documentation.",
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
parser.add_argument(
|
|
505
|
+
"--version", "-v",
|
|
506
|
+
action="store_true",
|
|
507
|
+
help="Show version and exit",
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
parser.add_argument(
|
|
511
|
+
"--fancy", "-f",
|
|
512
|
+
action="store_true",
|
|
513
|
+
help="Enable ANSI colors for modern terminals",
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
parser.add_argument(
|
|
517
|
+
"--visual", "-V",
|
|
518
|
+
action="store_true",
|
|
519
|
+
help="Show detailed ASCII diagrams and visual feedback",
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
parser.add_argument(
|
|
523
|
+
"--lang", "-l",
|
|
524
|
+
type=str,
|
|
525
|
+
default=DEFAULT_LANG,
|
|
526
|
+
choices=get_supported_languages(),
|
|
527
|
+
help="Output language (en, ar-gulf, ar-hejaz, es, ru)",
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
531
|
+
|
|
532
|
+
# Encode command
|
|
533
|
+
encode_parser = subparsers.add_parser(
|
|
534
|
+
"encode",
|
|
535
|
+
help="Encode components into an RPP address",
|
|
536
|
+
)
|
|
537
|
+
encode_parser.add_argument("--shell", "-s", type=int, required=True, help=f"Shell tier (0-{MAX_SHELL})")
|
|
538
|
+
encode_parser.add_argument("--theta", "-t", type=int, required=True, help=f"Theta sector (0-{MAX_THETA})")
|
|
539
|
+
encode_parser.add_argument("--phi", "-p", type=int, required=True, help=f"Phi grounding (0-{MAX_PHI})")
|
|
540
|
+
encode_parser.add_argument("--harmonic", "-H", type=int, required=True, help=f"Harmonic (0-{MAX_HARMONIC})")
|
|
541
|
+
encode_parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
542
|
+
encode_parser.set_defaults(func=cmd_encode)
|
|
543
|
+
|
|
544
|
+
# Decode command
|
|
545
|
+
decode_parser = subparsers.add_parser(
|
|
546
|
+
"decode",
|
|
547
|
+
help="Decode an RPP address into components",
|
|
548
|
+
)
|
|
549
|
+
decode_parser.add_argument("--address", "-a", type=str, required=True, help="Address (hex or decimal)")
|
|
550
|
+
decode_parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
551
|
+
decode_parser.set_defaults(func=cmd_decode)
|
|
552
|
+
|
|
553
|
+
# Resolve command
|
|
554
|
+
resolve_parser = subparsers.add_parser(
|
|
555
|
+
"resolve",
|
|
556
|
+
help="Resolve an RPP address to a routing decision",
|
|
557
|
+
)
|
|
558
|
+
resolve_parser.add_argument("--address", "-a", type=str, required=True, help="Address (hex or decimal)")
|
|
559
|
+
resolve_parser.add_argument("--operation", "-o", type=str, default="read", choices=["read", "write", "delete"], help="Operation type")
|
|
560
|
+
resolve_parser.add_argument("--consent", "-c", type=str, choices=["none", "diminished", "full", "explicit"], help="Consent level")
|
|
561
|
+
resolve_parser.add_argument("--emergency", "-e", action="store_true", help="Emergency override")
|
|
562
|
+
resolve_parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
|
|
563
|
+
resolve_parser.set_defaults(func=cmd_resolve)
|
|
564
|
+
|
|
565
|
+
# Demo command
|
|
566
|
+
demo_parser = subparsers.add_parser(
|
|
567
|
+
"demo",
|
|
568
|
+
help="Run demonstration of core scenarios",
|
|
569
|
+
)
|
|
570
|
+
demo_parser.set_defaults(func=cmd_demo)
|
|
571
|
+
|
|
572
|
+
# Version command
|
|
573
|
+
version_parser = subparsers.add_parser(
|
|
574
|
+
"version",
|
|
575
|
+
help="Show version",
|
|
576
|
+
)
|
|
577
|
+
version_parser.set_defaults(func=cmd_version)
|
|
578
|
+
|
|
579
|
+
# Tutorial command
|
|
580
|
+
tutorial_parser = subparsers.add_parser(
|
|
581
|
+
"tutorial",
|
|
582
|
+
help="Interactive tutorial explaining RPP concepts",
|
|
583
|
+
)
|
|
584
|
+
tutorial_parser.set_defaults(func=cmd_tutorial)
|
|
585
|
+
|
|
586
|
+
return parser
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
590
|
+
"""Main entry point."""
|
|
591
|
+
parser = build_parser()
|
|
592
|
+
args = parser.parse_args(argv)
|
|
593
|
+
|
|
594
|
+
# Handle --version flag at top level
|
|
595
|
+
if args.version:
|
|
596
|
+
return cmd_version(args)
|
|
597
|
+
|
|
598
|
+
# No command specified
|
|
599
|
+
if args.command is None:
|
|
600
|
+
parser.print_help()
|
|
601
|
+
return EXIT_SUCCESS
|
|
602
|
+
|
|
603
|
+
# Run command
|
|
604
|
+
return args.func(args)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
if __name__ == "__main__":
|
|
608
|
+
sys.exit(main())
|