zigmini 0.8.0-beta.6 → 0.8.0-beta.7

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 (49) hide show
  1. package/README.ko.md +77 -19
  2. package/README.md +78 -19
  3. package/assets/agent/gateway-request-rules.md +1 -0
  4. package/assets/agent/gateway-rules.md +6 -0
  5. package/assets/android/zigmini.apk +0 -0
  6. package/assets/android/zigmini.apk.json +3 -3
  7. package/assets/build.json +1 -1
  8. package/docs/manual.ko.md +23 -1
  9. package/docs/manual.md +23 -1
  10. package/host/omo-bridge/task-control.ts +4 -0
  11. package/host/omo-check/patches/omo-5.1.35-compaction-idle.patch +77 -0
  12. package/host/omo-check/patches/omo-5.1.35-compaction-intent.patch +212 -0
  13. package/host/omo-check/patches/omo-5.1.35-compaction-model.patch +299 -0
  14. package/host/omo-check/patches/omo-5.1.35-compaction-preservation.patch +1063 -0
  15. package/host/omo-check/patches/omo-5.1.35-compaction-refusal.patch +83 -0
  16. package/host/omo-check/patches/omo-5.1.35-gateway-port.manifest.json +169 -7
  17. package/host/omo-check/patches/omo-5.1.35-request-rules.patch +109 -0
  18. package/host/omo-check/patches/omo-5.1.35-turn-stats.patch +76 -0
  19. package/package.json +4 -1
  20. package/scripts/secret-rules.ts +101 -0
  21. package/scripts/secret-scan.allow.json +155 -0
  22. package/scripts/secret-scan.rules.json +30 -0
  23. package/src/agent-request-rules.ts +82 -0
  24. package/src/cli/access.ts +31 -34
  25. package/src/cli/launchd.ts +30 -4
  26. package/src/cli/main.ts +3 -43
  27. package/src/cli/messages.ts +160 -0
  28. package/src/cli/pair.ts +32 -25
  29. package/src/cli/setup.ts +164 -75
  30. package/src/decisions.ts +20 -1
  31. package/src/hub.ts +140 -30
  32. package/src/main.ts +43 -2
  33. package/src/omo/client.ts +28 -0
  34. package/src/protocol.ts +30 -1
  35. package/src/reply-notifications.ts +144 -0
  36. package/src/runtime/paths.ts +2 -0
  37. package/src/security-check.ts +241 -0
  38. package/src/store.ts +71 -1
  39. package/src/turn-stats.ts +49 -0
  40. package/src/work-watch.ts +36 -10
  41. package/src/work.ts +29 -4
  42. package/web/app.js +158 -0
  43. package/web/decisions.js +3 -0
  44. package/web/index.html +16 -0
  45. package/web/notifications.d.ts +25 -0
  46. package/web/notifications.js +118 -0
  47. package/web/style.css +12 -0
  48. package/web/turn-stats.d.ts +26 -0
  49. package/web/turn-stats.js +58 -0
@@ -0,0 +1,155 @@
1
+ {
2
+ "allow": [
3
+ {"cidr":"127.0.0.0/8","reason":"loopback addresses are not personal"},
4
+ {"cidr":"0.0.0.0/32","reason":"bind-all address used in listen settings, not a host"},
5
+ {"cidr":"192.0.2.0/24","reason":"RFC 5737 documentation range (TEST-NET-1)"},
6
+ {"cidr":"198.51.100.0/24","reason":"RFC 5737 documentation range (TEST-NET-2)"},
7
+ {"cidr":"203.0.113.0/24","reason":"RFC 5737 documentation range (TEST-NET-3)"},
8
+ {"cidr":"10.0.2.0/24","reason":"Android emulator NAT range, same on every machine"},
9
+ {"domain":"example.com","reason":"RFC 2606 reserved example domain"},
10
+ {"value":"ghp_ZGWsecretSCANallowFIXTURE0000000000000","reason":"secret-scan test fixture: proves allow-listed values are not reported"},
11
+ {"value":"Abc_-0123456789abcdefghijklmnopqrstuvwxyzAB","reason":"synthetic enrollment token in Android test EnrollmentGuardTest.kt (alphabet sequence, not a credential)"},
12
+ {"value":"215776236+owen-ever@users.noreply.github.com","reason":"public repository GitHub noreply commit address, not a personal inbox"},
13
+ {"domain":"example.net","reason":"reserved documentation/test domain; no owner endpoint"},
14
+ {"domain":"example.org","reason":"reserved documentation/test domain; no owner endpoint"},
15
+ {"domain":"example.invalid","reason":"reserved documentation/test domain; no owner endpoint"},
16
+ {"domain":"invalid","reason":"reserved documentation/test domain; no owner endpoint"},
17
+ {"domain":"test","reason":"reserved documentation/test domain; no owner endpoint"},
18
+ {"value":"localhost","reason":"loopback hostname, not an owner endpoint"},
19
+ {"value":"192.168.0.7","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
20
+ {"value":"8.8.8.8","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
21
+ {"value":"10.0.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
22
+ {"value":"10.255.255.255","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
23
+ {"value":"172.16.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
24
+ {"value":"172.31.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
25
+ {"value":"192.168.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
26
+ {"value":"100.64.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
27
+ {"value":"100.127.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
28
+ {"value":"9.0.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
29
+ {"value":"11.0.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
30
+ {"value":"172.15.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
31
+ {"value":"172.32.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
32
+ {"value":"171.16.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
33
+ {"value":"173.16.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
34
+ {"value":"192.167.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
35
+ {"value":"192.169.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
36
+ {"value":"191.168.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
37
+ {"value":"193.168.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
38
+ {"value":"100.63.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
39
+ {"value":"100.128.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
40
+ {"value":"99.64.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
41
+ {"value":"101.64.0.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
42
+ {"value":"fc00::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
43
+ {"value":"fdff:ffff::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
44
+ {"value":"zig.tail.ts.net","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
45
+ {"value":"mac.local","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
46
+ {"value":"172.31.255.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
47
+ {"value":"192.168.1.10","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
48
+ {"value":"100.127.255.254","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
49
+ {"value":"10.0.0.2","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
50
+ {"value":"10.0.0.9","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
51
+ {"value":"192.168.0.8","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
52
+ {"value":"fd12:3456::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
53
+ {"value":"fe80::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
54
+ {"value":"zig.tail1234.ts.net","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
55
+ {"value":"mac-mini.local","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
56
+ {"value":"redacted","rule":"internal-url","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
57
+ {"value":"incorrect","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
58
+ {"value":"100.101.102.103","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
59
+ {"value":"255.255.255.0","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
60
+ {"value":"192.168.0.9","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
61
+ {"value":"192.168.0.5","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/","CHANGELOG.md"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
62
+ {"value":"10.0.0.7","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
63
+ {"value":"100.64.0.2","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
64
+ {"value":"100.64.0.3","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
65
+ {"value":"101.64.0.2","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
66
+ {"value":"192.168.1.20","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
67
+ {"value":"fd7a:115c::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
68
+ {"value":"fd7a::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
69
+ {"value":"mac.tail1234.ts.net","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
70
+ {"value":"a.ts.net","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
71
+ {"value":"host.docker.internal","rule":"internal-host","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
72
+ {"value":"h","rule":"internal-url","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
73
+ {"value":"10.9.9.9","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
74
+ {"value":"100.100.1.2","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
75
+ {"value":"172.31.255.255","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
76
+ {"value":"1.1.1.1","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
77
+ {"value":"2.2.2.2","rule":"ipv4","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
78
+ {"value":"fd00::1","rule":"ipv6-internal","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
79
+ {"value":"nope","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
80
+ {"value":"fixture-token","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
81
+ {"value":"hunter2","rule":"credential-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
82
+ {"value":"claude-token","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
83
+ {"value":"plain","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
84
+ {"value":"wrong","rule":"authorization","files":[".omo/evidence/","scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/"],"reason":"synthetic test fixture; exact value in the named tests and their recorded mutation evidence only"},
85
+ {"value":"host.docker.internal","rule":"internal-host","reason":"Docker built-in host alias shared by all installations, not an owner hostname"},
86
+ {"value":"i.internal","rule":"internal-host","files":["src/cli/access.ts",".omo/evidence/","scripts/secret-scan.allow.json","src/enroll.ts"],"reason":"NetworkInterfaceInfo boolean property access, not a hostname"},
87
+ {"value":"string","rule":"device-serial","reason":"TypeScript type annotation, not a device serial"},
88
+ {"value":"String","rule":"device-serial","reason":"Kotlin type annotation, not a device serial"},
89
+ {"value":"127.0.0.1","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
90
+ {"value":"203.0.113.5","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
91
+ {"value":"localhost","rule":"internal-url","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
92
+ {"value":"0.0.0.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
93
+ {"value":"192.0.2.5","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
94
+ {"value":"10.9.8.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
95
+ {"value":"172.19.8.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
96
+ {"value":"192.168.8.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
97
+ {"value":"100.65.8.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
98
+ {"value":"192.0.2.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
99
+ {"value":"198.51.100.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
100
+ {"value":"203.0.113.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
101
+ {"value":"10.0.2.2","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
102
+ {"value":"1.2.3.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
103
+ {"value":"1.2.3.4","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
104
+ {"value":"255.255.255.255","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
105
+ {"value":"127.9.8.7","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
106
+ {"value":"127.0.0.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
107
+ {"value":"128.0.0.1","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
108
+ {"value":"192.0.2.255","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
109
+ {"value":"192.0.2.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
110
+ {"value":"192.0.3.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
111
+ {"value":"0.0.0.1","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
112
+ {"value":"8.8.8.9","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
113
+ {"value":"8.8.8.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
114
+ {"value":"10.0.2.0","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
115
+ {"value":"10.0.2.15","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
116
+ {"value":"10.0.3.15","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
117
+ {"value":"fd12::7","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
118
+ {"value":"fc12:3456::7","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
119
+ {"value":"fe80::7%en0","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
120
+ {"value":"fe90::7","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
121
+ {"value":"fea0::7","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
122
+ {"value":"feb0::7","rule":"ipv6-internal","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
123
+ {"value":"192.0.2.10","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
124
+ {"value":"127.0.0.2","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
125
+ {"value":"127.0.0.19","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic test input, including address-range boundaries; exact value only in tests and their evidence"},
126
+ {"value":"5a45f64c493ea432","rule":"device-serial","files":[".omo/evidence/npm-distribution/task-14-npm-distribution.txt","scripts/secret-scan.allow.json"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
127
+ {"value":"88e1ab9c9b8773d3","rule":"device-serial","files":[".omo/evidence/npm-distribution/task-15-npm-distribution.txt","scripts/secret-scan.allow.json"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
128
+ {"value":"0b70b55904aff5f2","rule":"device-serial","files":[".omo/evidence/npm-distribution/task-15-npm-distribution.txt","scripts/secret-scan.allow.json"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
129
+ {"value":"fc00::","rule":"ipv6-internal","files":[".omo/evidence/npm-distribution/task-16-npm-distribution.txt","scripts/secret-scan.allow.json","android/core/src/main/kotlin/dev/zigmini/core/Enrollment.kt"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
130
+ {"value":"fe80::","rule":"ipv6-internal","files":[".omo/evidence/npm-distribution/task-16-npm-distribution.txt","scripts/secret-scan.allow.json"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
131
+ {"value":"gw-a.local","rule":"internal-host","files":["scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/",".omo/evidence/"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
132
+ {"value":"gw-b.local","rule":"internal-host","files":["scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/",".omo/evidence/"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
133
+ {"value":"a1.s","rule":"authorization","files":[".omo/evidence/npm-distribution/task-16-npm-distribution.txt","scripts/secret-scan.allow.json",".omo/evidence/npm-distribution/task-19-npm-distribution.txt"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
134
+ {"value":"b1.s","rule":"authorization","files":["scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/",".omo/evidence/"],"reason":"destroyed isolated QA gateway or fake Robolectric gateway; not an owner device or credential"},
135
+ {"value":"5.1.30.1","rule":"ipv4","files":["scripts/secret-scan.allow.json","test/","android/app/src/test/","android/core/src/test/",".omo/evidence/"],"reason":"four-part synthetic version from a schema comparison test, not a host"},
136
+ {"value":"machine.tailnet-name.ts.net","rule":"internal-host","files":[".omo/ulw-research/20260925-152742/wave-2-lane-access-control-FULL.md","scripts/secret-scan.allow.json",".omo/senpi-task/tasks/"],"reason":"explicit documentation example, not an owner endpoint"},
137
+ {"value":"machine.tailnet.ts.net","rule":"internal-host","files":[".omo/ulw-research/20260925-152742/wave-2-lane-access-control-FULL.md","scripts/secret-scan.allow.json",".omo/senpi-task/tasks/"],"reason":"explicit documentation example, not an owner endpoint"},
138
+ {"value":"192.168.0.12","rule":"ipv4","files":["docs/second-mac-check.ko.md","scripts/secret-scan.allow.json","docs/second-mac-check.md"],"reason":"explicit documentation example, not an owner endpoint"},
139
+ {"value":"100.64.0.0","rule":"ipv4","files":[".omo/plans/npm-distribution.md","scripts/secret-scan.allow.json"],"reason":"four-part software version in the distribution plan, not a host"},
140
+ {"value":"before_parking_fd9a4a5f-5b22-4ce0-8619-77d85ed90666","rule":"high-entropy-assignment","files":[".omo/evidence/parking-log-e0a3011b/real-run1.log","scripts/secret-scan.allow.json"],"reason":"before_parking UUID test marker, previously reviewed in task-5-npm-distribution.json secretVerdict; not authentication"},
141
+ {"value":"redacted","rule":"internal-url","reason":"redaction marker, not a reachable hostname"},
142
+ {"value":"zigmini@users.noreply.github.com","rule":"email","files":["docs/patch-notes-next.md","scripts/secret-scan.allow.json"],"reason":"synthetic Git author example in bug 163 documentation; not an owner inbox"},
143
+ {"value":"before_parking_a9b60dfc-b7a1-4f25-94f5-d9ca5786f0a7","rule":"high-entropy-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"before_parking UUID preservation-test marker; task-5 secretVerdict reviewed it as non-authentication"},
144
+ {"value":"redacted/.zigmini-gateway/secrets/omo-ws-token","rule":"high-entropy-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"redacted file-path placeholder, not a token"},
145
+ {"value":"before_parking_f9d8e423-af40-45a0-9f7c-3adc50cdf94a","rule":"high-entropy-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"before_parking UUID preservation-test marker; task-5 secretVerdict reviewed it as non-authentication"},
146
+ {"value":"npm-oidc-no-reply@github.com","rule":"email","files":[".omo/evidence/omo-5112-rollout-09740f57/registry-metadata.json","scripts/secret-scan.allow.json"],"reason":"public upstream npm publisher/maintainer metadata (GitHub Actions / omo-ai); not the owner inbox"},
147
+ {"value":"code.yeon.gyu@gmail.com","rule":"email","files":[".omo/evidence/omo-5112-rollout-09740f57/registry-metadata.json","scripts/secret-scan.allow.json"],"reason":"public upstream npm publisher/maintainer metadata (GitHub Actions / omo-ai); not the owner inbox"},
148
+ {"value":"d60c8f533040762fc4fc20f4a0dbb4dcf6b8466bc1f5b490ef6979afa17aa403","rule":"high-entropy-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"SHA-256 comparison digest, not auth.json or a credential; task-5 secretVerdict reviewed it"},
149
+ {"value":"before_parking_0e4af03e-d19f-4130-8b33-afab3f863f8f","rule":"high-entropy-assignment","files":[".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"before_parking UUID preservation-test marker; task-5 secretVerdict reviewed it as non-authentication"},
150
+ {"value":"10.20.30.40","rule":"ipv4","files":["test/","android/app/src/test/","android/core/src/test/",".omo/evidence/","scripts/secret-scan.allow.json"],"reason":"synthetic held-card rendering test input, not an owner endpoint"},
151
+ {"value":"device-id","rule":"device-serial","files":["src/security-check.ts","scripts/secret-scan.allow.json"],"reason":"public finding-kind label in the security card kind map, not a device serial"},
152
+ {"value":"redacted75","rule":"internal-url","reason":"exact redacted endpoint placeholder in rewritten history; not an owner host"},
153
+ {"value":"redacted51","rule":"internal-url","reason":"exact redacted endpoint placeholder in rewritten history; not an owner host"}
154
+ ]
155
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "rules": [
3
+ { "id": "private-key", "severity": "secret", "pattern": "-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----" },
4
+ { "id": "github-token", "severity": "secret", "pattern": "\\b(?:gh[pousr]_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{22,})\\b" },
5
+ { "id": "npm-token", "severity": "secret", "pattern": "\\bnpm_[A-Za-z0-9]{36}\\b" },
6
+ { "id": "anthropic-key", "severity": "secret", "pattern": "\\bsk-ant-[A-Za-z0-9_-]{20,}" },
7
+ { "id": "openai-key", "severity": "secret", "pattern": "\\bsk-(?!ant-)(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}" },
8
+ { "id": "notion-token", "severity": "secret", "pattern": "\\b(?:secret_[A-Za-z0-9]{43}|ntn_[A-Za-z0-9]{40,})\\b" },
9
+ { "id": "slack-token", "severity": "secret", "pattern": "\\bxox[abposr]-[A-Za-z0-9-]{10,}" },
10
+ { "id": "slack-webhook", "severity": "secret", "pattern": "https://hooks\\.slack\\.com/services/[A-Za-z0-9_/]{20,}" },
11
+ { "id": "aws-access-key", "severity": "secret", "pattern": "\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b" },
12
+ { "id": "jwt", "severity": "secret", "pattern": "\\beyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}" },
13
+ { "id": "high-entropy-assignment", "severity": "secret", "pattern": "(?:token|secret|password|passwd|api[_-]?key|auth)[A-Za-z0-9_]*[\"']?\\s*[:=]\\s*[\"'](?![/~])([A-Za-z0-9+/_=.-]{32,})[\"']", "flags": "i", "group": 1, "minEntropy": 3.5 },
14
+ { "id": "email", "severity": "personal", "pattern": "\\b(?!this@|super@)[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)*\\.[A-Za-z]{2,}\\b" },
15
+ { "id": "ipv4", "severity": "personal", "pattern": "(?<![\\d.])(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?![\\d.])" },
16
+ { "id": "user-home-path", "severity": "personal", "pattern": "/Users/(?!Shared/)[A-Za-z0-9._-]+/" },
17
+ { "id": "ssh-public-key", "severity": "personal", "pattern": "(?:^|[ \\t])(?:(?:@cert-authority|@revoked)[ \\t]+)?(?:[^\\s]+[ \\t]+)?(?:ssh-(?:rsa|ed25519|dss)|ecdsa-sha2-[A-Za-z0-9-]+|sk-(?:ssh-ed25519|ecdsa-sha2-nistp256)@openssh\\.com)[ \\t]+[A-Za-z0-9+/]{20,}={0,3}" },
18
+ { "id": "ipv6-internal", "severity": "personal", "pattern": "(?<![A-Za-z0-9:])(?:f[cd][0-9a-f]{2}|fe[89ab][0-9a-f]):[0-9a-f:]+(?:%[A-Za-z0-9_.-]+)?(?![A-Za-z0-9:])", "flags": "i" },
19
+ { "id": "internal-host", "severity": "personal", "pattern": "(?<![A-Za-z0-9_.-])(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?:local|lan|internal|home\\.arpa|ts\\.net)(?=\\.(?![A-Za-z0-9_.-])|[^A-Za-z0-9_.-]|$)", "flags": "i" },
20
+ { "id": "internal-url", "severity": "personal", "pattern": "\\b(?:https?|wss?|ssh|smb|ftp|redis|postgres(?:ql)?|mysql)://(?:[^\\s/@]+@)?([a-z][a-z0-9-]*):\\d{1,5}\\b", "flags": "i", "group": 1 },
21
+ { "id": "adb-device", "severity": "personal", "pattern": "\\badb-[A-Za-z0-9_-]+\\._adb-tls-(?:connect|pairing)\\._tcp\\b" },
22
+ { "id": "device-serial", "severity": "personal", "pattern": "\\b(?:serial(?:number|_number|_no)?|device[_-]?(?:id|serial)|UDID)[\\\"']?\\s*[:=]\\s*[\\\"']?([A-Za-z0-9][A-Za-z0-9_-]{5,})(?=[\\\"'\\s,;}]|$)", "flags": "i", "group": 1 },
23
+ { "id": "tailscale-key", "severity": "secret", "pattern": "\\btskey-(?:auth|api|client|oauth)-[A-Za-z0-9_-]{10,}\\b" },
24
+ { "id": "gcp-api-key", "severity": "secret", "pattern": "\\bAIza[A-Za-z0-9_-]{35}\\b" },
25
+ { "id": "credential-assignment", "severity": "secret", "pattern": "\\b(?:password|passwd|client_secret|aws_secret_access_key|session_id|session_token)[\\\"']?\\s*[:=]\\s*[\\\"']?([A-Za-z0-9][A-Za-z0-9+/_=.!@#$%^&*-]{3,})(?=[\\\"'\\s,;}]|$)", "flags": "i", "group": 1 },
26
+ { "id": "authorization", "severity": "secret", "pattern": "\\bauthorization[\\\"']?\\s*[:=]\\s*[\\\"']?(?:Bearer|Basic)\\s+([A-Za-z0-9_+/.=-]{4,})", "flags": "i", "group": 1 },
27
+ { "id": "cookie-header", "severity": "secret", "pattern": "\\b(?:set-cookie|cookie)[\\\"']?\\s*[:=]\\s*[\\\"']?([A-Za-z0-9_.-]+=[A-Za-z0-9_+/.%=-]+(?:;[ \\t]*[A-Za-z0-9_.-]+=[A-Za-z0-9_+/.%=-]+)*)", "flags": "i", "group": 1 },
28
+ { "id": "phone-number", "severity": "personal", "pattern": "(?<![A-Za-z0-9_+])(?:010[- .]?\\d{4}[- .]?\\d{4}|\\+82[- .]?(?:10[- .]?\\d{4}[- .]?\\d{4}|[1-9]\\d?[- .]?\\d{3,4}[- .]?\\d{4}))(?![A-Za-z0-9_])" }
29
+ ]
30
+ }
@@ -0,0 +1,82 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { readRulesFile } from "./agent-rules.ts";
3
+
4
+ export const PACKAGE_REQUEST_RULES_FILE = fileURLToPath(new URL("../assets/agent/gateway-request-rules.md", import.meta.url));
5
+ export const REQUEST_RULES_TAG = "zigmini-request-rules";
6
+
7
+ export interface ProviderRequestEvent {
8
+ readonly payload: unknown;
9
+ readonly model?: { readonly api: string };
10
+ readonly streamKind?: "main" | "auxiliary";
11
+ }
12
+
13
+ function record(value: unknown): value is Record<string, unknown> {
14
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15
+ }
16
+
17
+ function strings(value: unknown): value is string[] {
18
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
19
+ }
20
+
21
+ /** Captured by one extension instance. Updates take effect when a new instance loads. */
22
+ export function loadRequestRules(file = PACKAGE_REQUEST_RULES_FILE): string {
23
+ return readRulesFile(file).text;
24
+ }
25
+
26
+ /** Changes only the final request payload. Never modifies the session's prompt or messages. */
27
+ export function applyRequestRules(event: ProviderRequestEvent, rules: string): unknown {
28
+ const payload = event.payload;
29
+ if (event.streamKind !== "main" || rules === "" || !record(payload)) return payload;
30
+ const block = `<${REQUEST_RULES_TAG}>\n${rules}\n</${REQUEST_RULES_TAG}>`;
31
+ const append = (text: string): string => text.includes(block) ? text : `${text}${text === "" ? "" : "\n\n"}${block}`;
32
+ switch (event.model?.api) {
33
+ case "openai-responses":
34
+ case "openai-codex-responses":
35
+ case "azure-openai-responses":
36
+ return { ...payload, instructions: append(typeof payload.instructions === "string" ? payload.instructions : "") };
37
+ case "openai-completions": {
38
+ if (!Array.isArray(payload.messages)) return payload;
39
+ const at = payload.messages.findIndex((message) => record(message) && (message.role === "system" || message.role === "developer"));
40
+ if (at < 0) return { ...payload, messages: [{ role: "system", content: block }, ...payload.messages] };
41
+ return {
42
+ ...payload,
43
+ messages: payload.messages.map((message, index) => {
44
+ if (index !== at || !record(message)) return message;
45
+ if (typeof message.content === "string") return { ...message, content: append(message.content) };
46
+ if (!Array.isArray(message.content)) return message;
47
+ const present = message.content.some((part) => record(part) && part.type === "text" && part.text === block);
48
+ return present ? message : { ...message, content: [...message.content, { type: "text", text: block }] };
49
+ }),
50
+ };
51
+ }
52
+ case "anthropic-messages": {
53
+ if (!Array.isArray(payload.system)) return { ...payload, system: append(typeof payload.system === "string" ? payload.system : "") };
54
+ const present = payload.system.some((part) => record(part) && part.type === "text" && part.text === block);
55
+ return present ? payload : { ...payload, system: [...payload.system, { type: "text", text: block }] };
56
+ }
57
+ case "claude-sdk-oauth": {
58
+ const system = payload.systemPrompt;
59
+ // SDK snapshots are durable. Keep the same prompt prefix, but do not record this request decoration.
60
+ if (typeof system === "string") return { ...payload, systemPrompt: { type: "custom", prompt: append(system), snapshot: false } };
61
+ if (strings(system)) {
62
+ return { ...payload, systemPrompt: { type: "custom", prompt: system.includes(block) ? system : [...system, block], snapshot: false } };
63
+ }
64
+ if (system === undefined) {
65
+ return { ...payload, systemPrompt: { type: "custom", prompt: block, snapshot: false } };
66
+ }
67
+ if (!record(system)) return payload;
68
+ if (system.type === "preset") {
69
+ return { ...payload, systemPrompt: { ...system, append: append(typeof system.append === "string" ? system.append : ""), snapshot: false } };
70
+ }
71
+ if (system.type === "custom" && typeof system.prompt === "string") {
72
+ return { ...payload, systemPrompt: { ...system, prompt: append(system.prompt), snapshot: false } };
73
+ }
74
+ if (system.type === "custom" && strings(system.prompt)) {
75
+ return { ...payload, systemPrompt: { ...system, prompt: system.prompt.includes(block) ? system.prompt : [...system.prompt, block], snapshot: false } };
76
+ }
77
+ return payload;
78
+ }
79
+ default:
80
+ return payload;
81
+ }
82
+ }
package/src/cli/access.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { isPrivateIPv4, type Network } from "../enroll.ts";
4
+ import { type Language, message, resolveLanguage } from "./messages.ts";
4
5
 
5
6
  /**
6
7
  * External access choice (plan Todo 27). Setup and the web 설정 panel pick how the phone reaches the app port:
@@ -27,47 +28,42 @@ export interface ResolveDeps {
27
28
 
28
29
  export type Resolved = { readonly ok: true; readonly record: AccessRecord } | { readonly ok: false; readonly message: string };
29
30
 
30
- export const TAILSCALE_GUIDE = [
31
- "Tailscale이 이 맥에 없습니다. 아래 순서로 설치한 뒤 같은 명령을 다시 실행하세요.",
32
- " 1. 맥: https://tailscale.com/download/mac 에서 Tailscale을 설치하고 로그인합니다.",
33
- " 2. 폰: Play 스토어에서 Tailscale을 설치하고 맥과 같은 계정으로 로그인합니다.",
34
- " 3. Tailscale 관리 화면에서 MagicDNS를 켭니다(주소가 기기 이름으로 나옵니다).",
35
- ].join("\n");
31
+ export const TAILSCALE_GUIDE = message("ko", "tailscaleGuide");
36
32
 
37
33
  export function isAccessChoice(value: unknown): value is AccessChoice {
38
34
  return (ACCESS_CHOICES as readonly unknown[]).includes(value);
39
35
  }
40
36
 
41
37
  /** The owner's domain: only `https://host[:port]` with no path, query, fragment or user info (89 security note 3: TLS required). */
42
- export function parseDomain(text: string): { readonly url: string } | { readonly error: string } {
38
+ export function parseDomain(text: string, lang: Language = "ko"): { readonly url: string } | { readonly error: string } {
43
39
  let url: URL;
44
40
  try {
45
41
  url = new URL(text.trim());
46
42
  } catch {
47
- return { error: `자기 도메인 주소가 URL이 아닙니다: ${text}` };
43
+ return { error: message(lang, "domainUrl", { text }) };
48
44
  }
49
- if (url.protocol !== "https:") return { error: "자기 도메인은 https:// 주소만 받습니다. 앱의 인증 값이 평문으로 지나가지 않도록 TLS가 필요합니다." };
45
+ if (url.protocol !== "https:") return { error: message(lang, "domainTls") };
50
46
  if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || url.pathname !== "/") {
51
- return { error: "자기 도메인은 https://<도메인>[:포트] 형식만 받습니다(경로, ?, #, 사용자 정보 없이)." };
47
+ return { error: message(lang, "domainFormat") };
52
48
  }
53
49
  return { url: url.origin };
54
50
  }
55
51
 
56
- export function parseTailscale(stdout: string, appPort: number): { readonly url: string } | { readonly error: string } {
52
+ export function parseTailscale(stdout: string, appPort: number, lang: Language = "ko"): { readonly url: string } | { readonly error: string } {
57
53
  let status: { BackendState?: unknown; Self?: { DNSName?: unknown; TailscaleIPs?: unknown } } | null;
58
54
  try {
59
55
  status = JSON.parse(stdout) as typeof status;
60
56
  } catch {
61
- return { error: "tailscale status --json 출력을 읽지 못했습니다." };
57
+ return { error: message(lang, "tailscaleJson") };
62
58
  }
63
59
  if (status?.BackendState !== "Running") {
64
- return { error: `Tailscale이 연결되어 있지 않습니다(상태: ${String(status?.BackendState ?? "?")}). Tailscale 앱에서 로그인하고 연결하세요.` };
60
+ return { error: message(lang, "tailscaleState", { state: String(status?.BackendState ?? "?") }) };
65
61
  }
66
62
  const name = typeof status.Self?.DNSName === "string" ? status.Self.DNSName.replace(/\.$/, "") : "";
67
63
  const ips = Array.isArray(status.Self?.TailscaleIPs) ? status.Self.TailscaleIPs : [];
68
64
  const ip = ips.find((value): value is string => typeof value === "string" && /^100\.\d+\.\d+\.\d+$/.test(value));
69
65
  const host = name !== "" ? name : ip;
70
- if (host === undefined) return { error: "Tailscale에서 이 맥의 MagicDNS 이름과 100.x 주소를 찾지 못했습니다." };
66
+ if (host === undefined) return { error: message(lang, "tailscaleHost") };
71
67
  return { url: `http://${host}:${appPort}` };
72
68
  }
73
69
 
@@ -77,21 +73,21 @@ export function lanUrl(network: Network, appPort: number): string | null {
77
73
  return address === undefined ? null : `http://${address}:${appPort}`;
78
74
  }
79
75
 
80
- export function resolveAccess(choice: AccessChoice, domain: string | undefined, appPort: number, deps: ResolveDeps): Resolved {
76
+ export function resolveAccess(choice: AccessChoice, domain: string | undefined, appPort: number, deps: ResolveDeps, lang: Language = "ko"): Resolved {
81
77
  if (choice === "domain") {
82
- const parsed = parseDomain(domain ?? "");
78
+ const parsed = parseDomain(domain ?? "", lang);
83
79
  return "error" in parsed ? { ok: false, message: parsed.error } : { ok: true, record: { choice, url: parsed.url } };
84
80
  }
85
81
  if (choice === "lan") {
86
82
  const url = lanUrl(deps.network, appPort);
87
83
  return url === null
88
- ? { ok: false, message: "기본 경로 네트워크의 사설 IP(10.x, 172.16-31.x, 192.168.x)를 찾지 못했습니다. 집 Wi-Fi에 연결한 뒤 다시 실행하세요." }
84
+ ? { ok: false, message: message(lang, "lanMissing") }
89
85
  : { ok: true, record: { choice, url } };
90
86
  }
91
87
  const run = deps.tailscale();
92
- if (run === null) return { ok: false, message: TAILSCALE_GUIDE };
93
- if (run.code !== 0) return { ok: false, message: `tailscale status 가 종료 코드 ${run.code}로 실패했습니다. Tailscale 앱이 실행 중인지 확인하세요.` };
94
- const parsed = parseTailscale(run.stdout, appPort);
88
+ if (run === null) return { ok: false, message: message(lang, "tailscaleGuide") };
89
+ if (run.code !== 0) return { ok: false, message: message(lang, "tailscaleFailed", { code: run.code }) };
90
+ const parsed = parseTailscale(run.stdout, appPort, lang);
95
91
  return "error" in parsed ? { ok: false, message: parsed.error } : { ok: true, record: { choice, url: parsed.url } };
96
92
  }
97
93
 
@@ -119,13 +115,13 @@ export function appPortEnv(appPort: number): Record<string, string> {
119
115
  return { ZGW_LAN_PORT: String(appPort), ZGW_LAN_HOST: "0.0.0.0" };
120
116
  }
121
117
 
122
- export function proxyGuide(url: string, appPort: number): string {
118
+ export function proxyGuide(url: string, appPort: number, lang: Language = "ko"): string {
123
119
  return [
124
- "리버스 프록시가 아래 주소를 이 맥의 앱 포트로 넘기는지 확인하세요. Caddy 예시(Caddyfile 한 블록, 인증서는 Caddy가 받습니다):",
120
+ message(lang, "proxyIntro"),
125
121
  ` ${new URL(url).host} {`,
126
122
  ` \treverse_proxy 127.0.0.1:${appPort}`,
127
123
  " }",
128
- "공유기에서 443 포트를 프록시가 있는 기기로 넘겨야 합니다. 설정을 바꾼 뒤 `zigmini doctor`로 다시 확인합니다.",
124
+ message(lang, "proxyEnd"),
129
125
  ].join("\n");
130
126
  }
131
127
 
@@ -133,12 +129,12 @@ export type Fetch = (url: string, init?: RequestInit) => Promise<Response>;
133
129
  export type PingResult = { readonly ok: true } | { readonly ok: false; readonly detail: string };
134
130
 
135
131
  /** `GET <url>/api/enroll/ping` must answer 200 `{ ok: true }` through the proxy and its certificate. */
136
- export async function pingAccess(fetcher: Fetch, url: string, timeoutMs = PING_TIMEOUT_MS): Promise<PingResult> {
132
+ export async function pingAccess(fetcher: Fetch, url: string, timeoutMs = PING_TIMEOUT_MS, lang: Language = "ko"): Promise<PingResult> {
137
133
  try {
138
134
  const response = await fetcher(`${url}/api/enroll/ping`, { signal: AbortSignal.timeout(timeoutMs) });
139
135
  if (response.status !== 200) return { ok: false, detail: `HTTP ${response.status}` };
140
136
  const body = await response.json().catch(() => null) as { ok?: unknown } | null;
141
- return body?.ok === true ? { ok: true } : { ok: false, detail: "응답이 zigmini 게이트웨이의 것이 아님" };
137
+ return body?.ok === true ? { ok: true } : { ok: false, detail: message(lang, "pingBody") };
142
138
  } catch (error) {
143
139
  return { ok: false, detail: error instanceof Error ? error.message : String(error) };
144
140
  }
@@ -146,14 +142,14 @@ export async function pingAccess(fetcher: Fetch, url: string, timeoutMs = PING_T
146
142
 
147
143
  export interface AccessItem { readonly id: "access"; readonly label: string; readonly status: "ok" | "bad" | "off"; readonly detail: string }
148
144
 
149
- export async function accessItem(record: AccessRecord | null | "invalid", ping: (url: string) => Promise<PingResult>): Promise<AccessItem> {
150
- const label = "외부 접속";
151
- if (record === null) return { id: "access", label, status: "off", detail: "고르지 않음(앱 포트 꺼짐)" };
152
- if (record === "invalid") return { id: "access", label, status: "bad", detail: `${ACCESS_FILE}를 읽지 못했습니다. \`zigmini setup --access ...\`로 다시 고르세요` };
145
+ export async function accessItem(record: AccessRecord | null | "invalid", ping: (url: string) => Promise<PingResult>, lang: Language = "ko"): Promise<AccessItem> {
146
+ const label = message(lang, "accessLabel");
147
+ if (record === null) return { id: "access", label, status: "off", detail: message(lang, "accessOff") };
148
+ if (record === "invalid") return { id: "access", label, status: "bad", detail: message(lang, "accessRead", { file: ACCESS_FILE }) };
153
149
  const result = await ping(record.url);
154
150
  return result.ok
155
151
  ? { id: "access", label, status: "ok", detail: `${record.choice} ${record.url}` }
156
- : { id: "access", label, status: "bad", detail: `${record.choice} ${record.url} 확인 실패: ${result.detail}` };
152
+ : { id: "access", label, status: "bad", detail: message(lang, "accessCheck", { ...record, detail: result.detail }) };
157
153
  }
158
154
 
159
155
  /**
@@ -180,6 +176,7 @@ export interface AccessApiDeps extends ResolveDeps {
180
176
  * the launchd environment, so that stays with `zigmini setup --access`.
181
177
  */
182
178
  export async function handleAccessApi(req: Request, deps: AccessApiDeps): Promise<Response> {
179
+ const lang = resolveLanguage(deps.home, process.env);
183
180
  if (req.method === "GET") {
184
181
  const saved = readAccess(deps.home);
185
182
  return Response.json({ access: saved === "invalid" ? null : saved, invalid: saved === "invalid", appPort: deps.appPort });
@@ -187,17 +184,17 @@ export async function handleAccessApi(req: Request, deps: AccessApiDeps): Promis
187
184
  if (req.method !== "POST") return Response.json({ error: "method_not_allowed" }, { status: 405 });
188
185
  if (foreignOrigin(req.headers, deps.webPort)) return Response.json({ error: "forbidden_origin" }, { status: 403 });
189
186
  if (deps.appPort <= 0) {
190
- return Response.json({ error: "app_port_off", message: "앱 포트가 꺼져 있습니다. 맥 터미널에서 `zigmini setup --access <선택>`을 먼저 실행하세요." }, { status: 409 });
187
+ return Response.json({ error: "app_port_off", message: message(lang, "appOff") }, { status: 409 });
191
188
  }
192
189
  const body = await req.json().catch(() => null) as { choice?: unknown; domain?: unknown } | null;
193
- if (!isAccessChoice(body?.choice)) return Response.json({ error: "bad_choice", message: `선택은 ${ACCESS_CHOICES.join(", ")} 중 하나입니다.` }, { status: 400 });
190
+ if (!isAccessChoice(body?.choice)) return Response.json({ error: "bad_choice", message: message(lang, "apiChoice", { choices: ACCESS_CHOICES.join(", ") }) }, { status: 400 });
194
191
  const domain = typeof body.domain === "string" ? body.domain : undefined;
195
- const resolved = resolveAccess(body.choice, domain, deps.appPort, deps);
192
+ const resolved = resolveAccess(body.choice, domain, deps.appPort, deps, lang);
196
193
  if (!resolved.ok) return Response.json({ error: "unresolved", message: resolved.message }, { status: 422 });
197
194
  if (resolved.record.choice === "domain") {
198
195
  const result = await deps.ping(resolved.record.url);
199
196
  if (!result.ok) {
200
- return Response.json({ error: "ping_failed", message: `${resolved.record.url} 확인 실패: ${result.detail}\n${proxyGuide(resolved.record.url, deps.appPort)}` }, { status: 502 });
197
+ return Response.json({ error: "ping_failed", message: message(lang, "apiPing", { url: resolved.record.url, detail: result.detail, guide: proxyGuide(resolved.record.url, deps.appPort, lang) }) }, { status: 502 });
201
198
  }
202
199
  }
203
200
  writeAccess(deps.home, resolved.record);