claude-access-for-all 0.1.1 → 0.2.0

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.
package/README.md CHANGED
@@ -57,19 +57,20 @@ Choose an action from the interactive menu, or call one directly:
57
57
  ```sh
58
58
  claude-access doctor
59
59
  claude-access audit
60
- claude-access cli --timezone UTC
61
- claude-access app --timezone UTC
62
- claude-access web --browser chrome --timezone UTC
60
+ claude-access score
61
+ claude-access cli --timezone America/Los_Angeles
62
+ claude-access app --timezone America/Los_Angeles
63
+ claude-access web --browser chrome --timezone America/Los_Angeles
63
64
  claude-access inspect
64
65
  claude-access reminder --renewal 2026-12-01 --days-before 3 > renewal.ics
65
66
  open renewal.ics
66
67
  ```
67
68
 
68
- To install directly from GitHub, use `npm install -g github:AMark-CS/claude-access-for-all`. Use `claude-access help` to see the complete command list. Diagnostics display a readable summary by default; use `claude-access doctor --json` or `claude-access audit --json` for machine-readable output. `inspect` opens the local graphical browser inspector in your default browser. To check a separate browser profile, open its local file in that profile instead; see the [privacy inspector guide](docs/privacy-inspector.md).
69
+ To install directly from GitHub, use `npm install -g github:AMark-CS/claude-access-for-all`. Use `claude-access help` to see the complete command list. Diagnostics display a readable summary by default. `inspect` opens the local graphical browser inspector in your default browser. To check a separate browser profile, open its local file in that profile instead; see the [privacy inspector guide](docs/privacy-inspector.md).
69
70
 
70
71
  For the calendar reminder, replace the example date with the next billing date shown by your billing provider. The file contains one all-day review event with an alert; import it into Apple Calendar and check your notification settings. It neither cancels nor renews anything, and must be regenerated when the billing date changes.
71
72
 
72
- The diagnostics run offline and do not print environment-variable values. The scoped launchers request process-local timezone and language settings; Desktop and browsers may behave differently. The browser launcher opens Claude in a separate profile and prints its private data directory. Review the [privacy guide](docs/privacy-inspector.md) for limitations before use.
73
+ The diagnostics run offline and do not print environment-variable values. The `score` command uses the third-party reference weights for timezone and UTC offset only, with its partial denominator and unmeasured browser signals clearly labeled; it is not a ban prediction. Scoped launchers default to the Los Angeles timezone. The scoped launchers request process-local timezone and language settings; Desktop and browsers may behave differently. The browser launcher opens Claude in a separate profile and prints its private data directory. Review the [privacy guide](docs/privacy-inspector.md) for limitations before use.
73
74
 
74
75
  To run the source version directly without npm, use `python3 tools/access.py doctor` or `python3 tools/privacy.py audit` from a clone. Review the short [source](tools/access.py) before running it.
75
76
 
@@ -11,8 +11,9 @@ const help = `Claude Access for All
11
11
  Usage: claude-access [command] [options]
12
12
 
13
13
  menu Open the interactive menu (default)
14
- doctor [--json] Check local installation and configuration presence
15
- audit [--json] Inspect CLI environment signals locally
14
+ doctor Check local installation and configuration presence
15
+ audit Inspect CLI environment signals locally
16
+ score Score locally observable timezone signals
16
17
  cli [options] [-- ...] Launch Claude Code with scoped locale settings
17
18
  app [options] Launch Claude Desktop with scoped locale settings
18
19
  web [options] Launch Claude in a separate browser profile
@@ -23,8 +24,8 @@ Usage: claude-access [command] [options]
23
24
  version Show the package version
24
25
 
25
26
  Examples:
26
- claude-access cli --timezone UTC -- --version
27
- claude-access web --browser edge --timezone UTC
27
+ claude-access cli --timezone America/Los_Angeles -- --version
28
+ claude-access web --browser edge --timezone America/Los_Angeles
28
29
  claude-access reminder --renewal 2026-12-01 --days-before 3 > renewal.ics
29
30
 
30
31
  Requires macOS, Node.js 18+, and Python 3.9+. Launching Claude requires the
@@ -47,6 +48,7 @@ function dispatch(command, args = []) {
47
48
  switch (command) {
48
49
  case 'doctor': return run('python3', [join(root, 'tools/access.py'), 'doctor', ...args]);
49
50
  case 'audit': return run('python3', [join(root, 'tools/privacy.py'), 'audit', ...args]);
51
+ case 'score': return run('python3', [join(root, 'tools/score.py'), ...args]);
50
52
  case 'cli': case 'app': case 'web':
51
53
  return run('python3', [join(root, 'tools/privacy.py'), command, ...args]);
52
54
  case 'reminder': return run('python3', [join(root, 'tools/access.py'), 'reminder', ...args]);
@@ -69,10 +71,10 @@ async function menu() {
69
71
  }
70
72
  const rl = createInterface({ input: process.stdin, output: process.stdout });
71
73
  try {
72
- console.log(`\nClaude Access for All\n\n 1 Check local environment\n 2 Inspect CLI signals\n 3 Start Claude Code\n 4 Start Claude Desktop\n 5 Start Claude in a separate browser profile\n 6 Open browser signal inspector\n 7 Open the privacy guide\n 8 Create a renewal calendar file\n 0 Exit\n`);
74
+ console.log(`\nClaude Access for All\n\n 1 Check local environment\n 2 Inspect CLI signals\n 3 Start Claude Code\n 4 Start Claude Desktop\n 5 Start Claude in a separate browser profile\n 6 Open browser signal inspector\n 7 Open the privacy guide\n 8 Create a renewal calendar file\n 9 Score local timezone signals\n 0 Exit\n`);
73
75
  const choice = (await rl.question('Choose an option: ')).trim();
74
76
  if (choice === '0') return 0;
75
- const simple = { '1': 'doctor', '2': 'audit', '3': 'cli', '4': 'app', '6': 'inspect', '7': 'guide' };
77
+ const simple = { '1': 'doctor', '2': 'audit', '3': 'cli', '4': 'app', '6': 'inspect', '7': 'guide', '9': 'score' };
76
78
  if (simple[choice]) { rl.close(); return dispatch(simple[choice]); }
77
79
  if (choice === '5') {
78
80
  const browser = (await rl.question('Browser (chrome/edge) [chrome]: ')).trim() || 'chrome';
@@ -93,7 +95,7 @@ async function menu() {
93
95
  console.log(`Created ${output}. Import it into Calendar and check notification settings.`);
94
96
  return 0;
95
97
  }
96
- console.error('Unknown option. Run the menu again to choose 0–8.');
98
+ console.error('Unknown option. Run the menu again to choose 0–9.');
97
99
  return 2;
98
100
  } finally { rl.close(); }
99
101
  }
@@ -1,26 +1,30 @@
1
1
  # Client privacy inspector and scoped launchers
2
2
 
3
- Reviewed October 27, 2026. This toolkit borrows the idea of independently inspecting timezone, language, browser identity, and fonts from [Fuck Claude](https://fuck-claude.com/). Its scoring and claims are third-party interpretations, not verified Anthropic enforcement rules. This project does not copy its scoring algorithm or claim that these signals cause a ban.
3
+ Reviewed October 27, 2026. This toolkit borrows the idea of independently inspecting timezone, language, browser identity, and fonts from [Fuck Claude](https://fuck-claude.com/). Its scoring and claims are third-party interpretations, not verified Anthropic enforcement rules. The CLI adapts its timezone and offset scoring rules with attribution; the limited result is not a ban prediction. See the bundled [MIT notice](../third-party/FuckClaude-LICENSE.txt).
4
4
 
5
5
  Each client has its own process and network path. A change applied to Terminal does not necessarily affect Desktop or an existing browser. Nothing here changes the macOS system timezone, system language, global proxy settings, the installed Claude apps, or existing browser profiles.
6
6
 
7
+ ## Local partial score
8
+
9
+ Run `claude-access score` in the terminal to score the current process timezone and current UTC offset. The result uses the reference project's 24-point timezone rule and 3-point offset rule. It shows earned points and only the maximum for signals actually available, so it is **not** the reference browser's 100-point score. Other reference signals require a browser and are marked unmeasured; IP, credentials, billing and account state are not checked. A missing timezone is excluded rather than treated as zero. `TZ=America/Los_Angeles claude-access score` checks the requested launcher timezone in a child process, not your existing browser or Desktop application. Rules are third-party heuristics and do not establish an Anthropic enforcement decision.
10
+
7
11
  ## 1. Claude Code CLI
8
12
 
9
13
  ```sh
10
14
  python3 tools/privacy.py audit
11
- python3 tools/privacy.py cli --timezone UTC --locale en_US.UTF-8
15
+ python3 tools/privacy.py cli --timezone America/Los_Angeles --locale en_US.UTF-8
12
16
  # Pass CLI arguments after --, for example:
13
- python3 tools/privacy.py cli --timezone UTC -- --version
17
+ python3 tools/privacy.py cli --timezone America/Los_Angeles -- --version
14
18
  ```
15
19
 
16
20
  The audit reports the current shell's timezone names and whether known locale, routing, and authentication environment variables are set. It does not print their values. Treat even local timezone output as private. It cannot inspect values in Claude settings, other running processes, system proxies, DNS, or server-side checks.
17
21
 
18
- The launcher starts `claude` with `TZ`, `LANG`, and `LC_ALL` changed only in the new process and its child processes. Select another installed IANA timezone with `--timezone`, and another available locale with `--locale`. An invalid timezone is rejected. Locale availability and application behavior vary. Inherited endpoint, proxy, and authentication variables are preserved; check `/status` for the actual authentication route. Shell hooks, settings files, plugins and background services may affect behavior. In particular, a previously running Claude Code background supervisor can have its own environment.
22
+ The launcher defaults to `America/Los_Angeles` and starts `claude` with `TZ`, `LANG`, and `LC_ALL` changed only in the new process and its child processes. Select another installed IANA timezone with `--timezone`, and another available locale with `--locale`. An invalid timezone is rejected. Locale availability and application behavior vary. Inherited endpoint, proxy, and authentication variables are preserved; check `/status` for the actual authentication route. Shell hooks, settings files, plugins and background services may affect behavior. In particular, a previously running Claude Code background supervisor can have its own environment.
19
23
 
20
24
  ## 2. Claude Desktop for macOS
21
25
 
22
26
  ```sh
23
- python3 tools/privacy.py app --timezone UTC --locale en_US.UTF-8
27
+ python3 tools/privacy.py app --timezone America/Los_Angeles --locale en_US.UTF-8
24
28
  # If installed under a different name:
25
29
  python3 tools/privacy.py app --app '/Applications/Claude.app'
26
30
  ```
@@ -30,9 +34,9 @@ This asks macOS Launch Services to open a new app instance with the specified en
30
34
  ## 3. Claude on the web
31
35
 
32
36
  ```sh
33
- python3 tools/privacy.py web --browser chrome --timezone UTC --language en-US
37
+ python3 tools/privacy.py web --browser chrome --timezone America/Los_Angeles --language en-US
34
38
  # Or use Microsoft Edge:
35
- python3 tools/privacy.py web --browser edge --timezone UTC --language en-US
39
+ python3 tools/privacy.py web --browser edge --timezone America/Los_Angeles --language en-US
36
40
  ```
37
41
 
38
42
  The launcher creates a fresh, private browser data directory and opens `https://claude.ai/` in a separate profile. It prints that path locally. The profile can contain login cookies and other sensitive data; it is not deleted automatically. To reuse it, pass `--profile '/absolute/path/printed/earlier'`. Close the browser before manually deleting a profile you no longer need. Browser launches require the chosen browser to be installed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-access-for-all",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A macOS guide and local privacy toolkit for Claude Code, Desktop, and web",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,8 @@
14
14
  "docs/",
15
15
  "README.md",
16
16
  "LICENSE",
17
- "SECURITY.md"
17
+ "SECURITY.md",
18
+ "third-party/"
18
19
  ],
19
20
  "engines": {
20
21
  "node": ">=18"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LinXiaoTao (https://github.com/LinXiaoTao/FuckClaude)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/tools/access.py CHANGED
@@ -2,7 +2,6 @@
2
2
  """Offline environment checks and a one-time subscription review calendar."""
3
3
  import argparse
4
4
  from datetime import date, datetime, timedelta, timezone
5
- import json
6
5
  import os
7
6
  import platform
8
7
  import shutil
@@ -94,15 +93,14 @@ def reminder(renewal, lead, today=None):
94
93
  def main():
95
94
  parser = argparse.ArgumentParser(description=__doc__)
96
95
  commands = parser.add_subparsers(dest='command', required=True)
97
- diagnostic = commands.add_parser('doctor', help='Print an offline environment report.')
98
- diagnostic.add_argument('--json', action='store_true', help='Output machine-readable JSON.')
96
+ commands.add_parser('doctor', help='Print an offline environment report.')
99
97
  calendar = commands.add_parser('reminder', help='Print a one-time .ics calendar to stdout.')
100
98
  calendar.add_argument('--renewal', type=parse_date, required=True)
101
99
  calendar.add_argument('--days-before', type=days_before, default=3)
102
100
  args = parser.parse_args()
103
101
  if args.command == 'doctor':
104
102
  result = doctor()
105
- print(json.dumps(result, indent=2) if args.json else render_doctor(result))
103
+ print(render_doctor(result))
106
104
  else:
107
105
  try:
108
106
  content = reminder(args.renewal, args.days_before)
package/tools/privacy.py CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """Inspect and launch Claude clients with explicit, process-scoped locale settings."""
3
3
  import argparse
4
- import json
5
4
  import os
6
5
  from pathlib import Path
7
6
  import platform
@@ -67,11 +66,10 @@ def browser_command(browser, profile, language, timezone, locale):
67
66
  def main():
68
67
  parser = argparse.ArgumentParser(description=__doc__)
69
68
  sub = parser.add_subparsers(dest='command', required=True)
70
- diagnostic = sub.add_parser('audit', help='Show local signals without exposing secret values.')
71
- diagnostic.add_argument('--json', action='store_true', help='Output machine-readable JSON.')
69
+ sub.add_parser('audit', help='Show local signals without exposing secret values.')
72
70
  for name in ('cli', 'app', 'web'):
73
71
  command = sub.add_parser(name, help='Launch ' + name + ' with process-scoped settings.')
74
- command.add_argument('--timezone', type=validate_timezone, default='UTC')
72
+ command.add_argument('--timezone', type=validate_timezone, default='America/Los_Angeles')
75
73
  command.add_argument('--locale', type=validate_locale, default='en_US.UTF-8')
76
74
  if name == 'cli':
77
75
  command.add_argument('claude_args', nargs=argparse.REMAINDER)
@@ -84,7 +82,7 @@ def main():
84
82
  args = parser.parse_args()
85
83
  if args.command == 'audit':
86
84
  result = audit()
87
- print(json.dumps(result, indent=2) if args.json else render_audit(result))
85
+ print(render_audit(result))
88
86
  return
89
87
  if sys.platform != 'darwin':
90
88
  parser.error('Launch commands require macOS.')
package/tools/report.py CHANGED
@@ -1,4 +1,4 @@
1
- """Readable, privacy-conscious CLI reports. JSON remains available via --json."""
1
+ """Readable, privacy-conscious CLI reports."""
2
2
  import os
3
3
  import sys
4
4
 
package/tools/score.py ADDED
@@ -0,0 +1,99 @@
1
+ """Local partial signal score, adapted from LinXiaoTao/FuckClaude (MIT)."""
2
+ import argparse
3
+ import datetime
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from report import section, style, title
8
+
9
+ # Based on src/config/signals.ts in LinXiaoTao/FuckClaude, master as reviewed
10
+ # 2026-10-27. Browser-only measurements are deliberately not substituted.
11
+ MAINLAND_ZONES = frozenset(('Asia/Shanghai', 'Asia/Urumqi', 'Asia/Chongqing',
12
+ 'Asia/Chungking', 'Asia/Harbin', 'Asia/Kashgar'))
13
+ OTHER_ZONES = frozenset(('Asia/Hong_Kong', 'Asia/Macau'))
14
+
15
+
16
+ def timezone_name(env=None, localtime='/etc/localtime'):
17
+ env = os.environ if env is None else env
18
+ if 'TZ' in env:
19
+ # POSIX strings and empty values are not necessarily IANA timezones.
20
+ value = env['TZ'].lstrip(':')
21
+ if value == 'UTC':
22
+ return value
23
+ parts = value.split('/')
24
+ if len(parts) > 1 and all(part and part not in ('.', '..') for part in parts):
25
+ if (Path('/usr/share/zoneinfo') / value).is_file():
26
+ return value
27
+ return None
28
+ try:
29
+ target = str(Path(localtime).resolve(strict=True))
30
+ except (OSError, RuntimeError):
31
+ return None
32
+ marker = '/zoneinfo/'
33
+ return target.split(marker, 1)[1] if marker in target else None
34
+
35
+
36
+ def timezone_weight(name):
37
+ if name is None:
38
+ return None
39
+ if name in MAINLAND_ZONES:
40
+ return 1.0
41
+ if name in OTHER_ZONES:
42
+ return 0.6
43
+ return 0.0
44
+
45
+
46
+ def collect():
47
+ name = timezone_name()
48
+ offset = datetime.datetime.now().astimezone().utcoffset()
49
+ minutes = int(offset.total_seconds() / 60) if offset is not None else None
50
+ return {'timezone': name, 'offset_minutes': minutes}
51
+
52
+
53
+ def render_score(data):
54
+ name = data['timezone']
55
+ ratio = timezone_weight(name)
56
+ minutes = data['offset_minutes']
57
+ tz_points = None if ratio is None else ratio * 24
58
+ offset_points = None if minutes is None else (2.1 if minutes == 480 else 0)
59
+ values = [x for x in (tz_points, offset_points) if x is not None]
60
+ possible = (24 if tz_points is not None else 0) + (3 if offset_points is not None else 0)
61
+ points = sum(values)
62
+ bar_filled = round(points / possible * 24) if possible else 0
63
+ bar = '█' * bar_filled + '░' * (24 - bar_filled)
64
+ lines = title('Local signal score', 'Terminal-only snapshot · offline')
65
+ if possible:
66
+ lines += [' ' + style(f'{points:g} / {possible} points', '1;36'),
67
+ ' ' + style(bar, '36'),
68
+ ' Partial score · available signals only · not a 100-point result', '']
69
+ else:
70
+ lines += [' Score unavailable · no supported signals could be read', '']
71
+ lines += [section('Measured signals')]
72
+ lines += [f" {'Timezone':<16} {(name or 'Unavailable'):<28} " +
73
+ (f'{tz_points:g} / 24' if tz_points is not None else 'not scored')]
74
+ if minutes is None:
75
+ offset_text = 'Unavailable'
76
+ else:
77
+ hours, mins = divmod(abs(minutes), 60)
78
+ offset_text = f"UTC{'+' if minutes >= 0 else '-'}{hours:02d}:{mins:02d}"
79
+ lines += [f" {'UTC offset':<16} {offset_text:<28} " +
80
+ (f'{offset_points:g} / 3' if offset_points is not None else 'not scored')]
81
+ lines += [section('Not measured'),
82
+ ' Browser language & Intl locale Browser fonts & vendor fonts',
83
+ ' Browser / device identity WebRTC & emoji rendering',
84
+ '',
85
+ ' This is a third-party heuristic, not an Anthropic risk score or',
86
+ ' ban prediction. It cannot assess IP, billing or account status.',
87
+ ' Browser signals need a browser; run claude-access inspect.',
88
+ ' To inspect the launcher timezone, run:',
89
+ ' TZ=America/Los_Angeles claude-access score', '']
90
+ return '\n'.join(lines)
91
+
92
+
93
+ def main():
94
+ argparse.ArgumentParser(description=__doc__).parse_args()
95
+ print(render_score(collect()))
96
+
97
+
98
+ if __name__ == '__main__':
99
+ main()