ai-usage-bar-mac 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 noah-traderlab
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.
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-usage-bar-mac
3
+ Version: 0.1.0
4
+ Summary: macOS team companion for AI Usage Bar
5
+ Author: noah-traderlab
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/noah-traderlab/ai-usage-bar
8
+ Project-URL: Source, https://github.com/noah-traderlab/ai-usage-bar
9
+ Project-URL: Issues, https://github.com/noah-traderlab/ai-usage-bar/issues
10
+ Project-URL: Security, https://github.com/noah-traderlab/ai-usage-bar/security/policy
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ License-File: THIRD_PARTY_NOTICES.md
18
+ Dynamic: license-file
19
+
20
+ # AI Usage Bar — macOS companion
21
+
22
+ An early-stage Python companion to the Windows AI Usage Bar application. It joins an existing team and reports Claude Code/Codex usage to a shared folder. The administrator dashboard is part of the separate Windows application, not this package.
23
+
24
+ Requires Python 3.11 or newer. The optional GUI needs a Python installation with Tk support. macOS GUI behavior on end-user machines is not yet verified. Linux and Windows are used for automated checks; their native desktop integration is not supported by this companion.
25
+
26
+ ## Install and inspect
27
+
28
+ After this package is published on PyPI:
29
+
30
+ ```sh
31
+ python3 -m venv .venv
32
+ .venv/bin/python -m pip install ai-usage-bar-mac
33
+ .venv/bin/aiusagebar --help
34
+ ```
35
+
36
+ Until publication, build/install the wheel from the source repository instead. This document does not claim that a registry release already exists.
37
+
38
+ ## Privacy and use
39
+
40
+ Joining a team is optional and requires explicit consent. Team reports can include machine/user identifiers, email, token usage, session titles, working directories, activity times, and prompt text from recent Claude Code/Codex sessions. Read the repository's macOS guide and the join consent before using `join --yes`; never run that option without understanding and agreeing to the sharing scope. Reports are encrypted for the team administrator. This does not remove the need to trust the administrator and shared-folder permissions.
41
+
42
+ Use `aiusagebar status` to inspect participation and `aiusagebar leave` to stop team participation. The optional friends feature shares a more limited status and is off by default. See the source repository for full usage, limitations, and security reporting instructions. Do not include private logs, keys, or personal information in public issues.
43
+
44
+ ## Maintenance and license
45
+
46
+ Maintained by [noah-traderlab](https://github.com/noah-traderlab). MIT licensed; see the bundled license and third-party notices. Investigation and implementation can use AI assistance with recorded checks. This is an independent project and is not affiliated with Anthropic or OpenAI. No CVE, independent security audit, adoption threshold, or external program eligibility is claimed.
@@ -0,0 +1,43 @@
1
+ # AI Usage Bar
2
+
3
+ Claude CodeとCodexの利用状況を表示するWindowsアプリと、Mac用のチーム参加・報告ツールです。
4
+
5
+ ## 構成
6
+
7
+ - `src/`: Windowsアプリ(C# / .NET Framework)
8
+ - `installer/`: Windowsインストーラー
9
+ - `mac/`: PythonのMac用ツール
10
+ - `tests/`: Windows用テスト
11
+ - `docs/`: 製品説明と仕様
12
+
13
+ Windows版は `build.ps1` と同梱の「はじめに」を参照してください。Mac版は `mac/はじめに(Mac).txt` を参照してください。
14
+
15
+ ## セキュリティ修正の検証
16
+
17
+ ```sh
18
+ cd mac
19
+ python3 -m unittest test_secure_writes -v
20
+ python3 selftest.py
21
+ ```
22
+
23
+ 2026-10-23の検証では、追加した3テストメソッドと既存36チェックが成功しています。GitHub ActionsのmacOS・Linux環境でも実行し、POSIXの0600権限を確認しました。MacのGUI操作を含む利用者端末での動作確認は未完了です。
24
+
25
+ チームの状態ファイルには秘密鍵が含まれます。状態ファイル・実際のログ・認証情報をリポジトリやIssueに添付しないでください。
26
+
27
+ ## 公開ソースの状態
28
+
29
+ このリポジトリは開発中のソース公開版です。利用者端末でのGUI動作確認は未完了で、動作保証付きのバイナリリリースではありません。
30
+
31
+ Windowsでの確認: 単体テスト305件、チーム機能147件、C#/Python相互運用30件が成功しています。CIの結果はGitHub Actionsを参照してください。
32
+
33
+ 元アイコンは出所・再配布許諾を確認できないため除外しました。カスタムアイコンなしでビルドできます。詳細は `THIRD_PARTY_NOTICES.md` を参照してください。
34
+
35
+ ClaudeやCodexなどの名称は対応先を示すものです。本ツールは独立したプロジェクトです。
36
+
37
+ ## ライセンス
38
+
39
+ [MIT License](LICENSE)。コード・文章の作成者による公開とMIT適用の承認に基づき公開しています。第三者のランタイムや商標を再許諾するものではありません。
40
+
41
+ ## Pythonパッケージの配布準備
42
+
43
+ Mac用ツールをwheel/sdistとしてビルドできるようにしました。PyPIへの公開・パッケージ名の確保はまだ行っていません。ビルド、インストール検査、公開手順と採用統計の扱いは [配布手順](docs/python-package-release.md) を参照してください。
@@ -0,0 +1,27 @@
1
+ # AI Usage Bar — macOS companion
2
+
3
+ An early-stage Python companion to the Windows AI Usage Bar application. It joins an existing team and reports Claude Code/Codex usage to a shared folder. The administrator dashboard is part of the separate Windows application, not this package.
4
+
5
+ Requires Python 3.11 or newer. The optional GUI needs a Python installation with Tk support. macOS GUI behavior on end-user machines is not yet verified. Linux and Windows are used for automated checks; their native desktop integration is not supported by this companion.
6
+
7
+ ## Install and inspect
8
+
9
+ After this package is published on PyPI:
10
+
11
+ ```sh
12
+ python3 -m venv .venv
13
+ .venv/bin/python -m pip install ai-usage-bar-mac
14
+ .venv/bin/aiusagebar --help
15
+ ```
16
+
17
+ Until publication, build/install the wheel from the source repository instead. This document does not claim that a registry release already exists.
18
+
19
+ ## Privacy and use
20
+
21
+ Joining a team is optional and requires explicit consent. Team reports can include machine/user identifiers, email, token usage, session titles, working directories, activity times, and prompt text from recent Claude Code/Codex sessions. Read the repository's macOS guide and the join consent before using `join --yes`; never run that option without understanding and agreeing to the sharing scope. Reports are encrypted for the team administrator. This does not remove the need to trust the administrator and shared-folder permissions.
22
+
23
+ Use `aiusagebar status` to inspect participation and `aiusagebar leave` to stop team participation. The optional friends feature shares a more limited status and is off by default. See the source repository for full usage, limitations, and security reporting instructions. Do not include private logs, keys, or personal information in public issues.
24
+
25
+ ## Maintenance and license
26
+
27
+ Maintained by [noah-traderlab](https://github.com/noah-traderlab). MIT licensed; see the bundled license and third-party notices. Investigation and implementation can use AI assistance with recorded checks. This is an independent project and is not affiliated with Anthropic or OpenAI. No CVE, independent security audit, adoption threshold, or external program eligibility is claimed.
@@ -0,0 +1,11 @@
1
+ # Third-party components and asset review
2
+
3
+ This source snapshot contains no bundled Python interpreter, .NET runtime, font files, or third-party dependency packages. Windows uses the installed .NET Framework and operating-system APIs. The Mac tool uses Python standard-library modules, including Tkinter when available. These runtimes remain subject to their own terms and are not relicensed by this project.
4
+
5
+ The source review found no third-party copyright or license headers. The creator confirmed authorship of the code and documentation and approved publication under MIT on 2026-10-23. The review itself is not an exhaustive external similarity or ownership audit.
6
+
7
+ The original `assets/AppIcon.ico` and its conversion script were omitted from this public-preparation snapshot because the source artwork and its permission to redistribute could not be established. The application can be built without a custom embedded icon. Task-tray graphics are drawn by the application; font files are not distributed.
8
+
9
+ Claude, Codex, Windows, macOS, and other product names identify integrations or supported environments. This is an independent tool; no endorsement or affiliation is claimed. This source snapshot does not grant rights to third-party names or trademarks.
10
+
11
+ Review date: 2026-10-23. This file is a provenance record, not a certification that all rights have been cleared.
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-usage-bar-mac
3
+ Version: 0.1.0
4
+ Summary: macOS team companion for AI Usage Bar
5
+ Author: noah-traderlab
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/noah-traderlab/ai-usage-bar
8
+ Project-URL: Source, https://github.com/noah-traderlab/ai-usage-bar
9
+ Project-URL: Issues, https://github.com/noah-traderlab/ai-usage-bar/issues
10
+ Project-URL: Security, https://github.com/noah-traderlab/ai-usage-bar/security/policy
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ License-File: THIRD_PARTY_NOTICES.md
18
+ Dynamic: license-file
19
+
20
+ # AI Usage Bar — macOS companion
21
+
22
+ An early-stage Python companion to the Windows AI Usage Bar application. It joins an existing team and reports Claude Code/Codex usage to a shared folder. The administrator dashboard is part of the separate Windows application, not this package.
23
+
24
+ Requires Python 3.11 or newer. The optional GUI needs a Python installation with Tk support. macOS GUI behavior on end-user machines is not yet verified. Linux and Windows are used for automated checks; their native desktop integration is not supported by this companion.
25
+
26
+ ## Install and inspect
27
+
28
+ After this package is published on PyPI:
29
+
30
+ ```sh
31
+ python3 -m venv .venv
32
+ .venv/bin/python -m pip install ai-usage-bar-mac
33
+ .venv/bin/aiusagebar --help
34
+ ```
35
+
36
+ Until publication, build/install the wheel from the source repository instead. This document does not claim that a registry release already exists.
37
+
38
+ ## Privacy and use
39
+
40
+ Joining a team is optional and requires explicit consent. Team reports can include machine/user identifiers, email, token usage, session titles, working directories, activity times, and prompt text from recent Claude Code/Codex sessions. Read the repository's macOS guide and the join consent before using `join --yes`; never run that option without understanding and agreeing to the sharing scope. Reports are encrypted for the team administrator. This does not remove the need to trust the administrator and shared-folder permissions.
41
+
42
+ Use `aiusagebar status` to inspect participation and `aiusagebar leave` to stop team participation. The optional friends feature shares a more limited status and is off by default. See the source repository for full usage, limitations, and security reporting instructions. Do not include private logs, keys, or personal information in public issues.
43
+
44
+ ## Maintenance and license
45
+
46
+ Maintained by [noah-traderlab](https://github.com/noah-traderlab). MIT licensed; see the bundled license and third-party notices. Investigation and implementation can use AI assistance with recorded checks. This is an independent project and is not affiliated with Anthropic or OpenAI. No CVE, independent security audit, adoption threshold, or external program eligibility is claimed.
@@ -0,0 +1,21 @@
1
+ LICENSE
2
+ README.md
3
+ README.pypi.md
4
+ THIRD_PARTY_NOTICES.md
5
+ pyproject.toml
6
+ mac/ai_usage_bar_mac.egg-info/PKG-INFO
7
+ mac/ai_usage_bar_mac.egg-info/SOURCES.txt
8
+ mac/ai_usage_bar_mac.egg-info/dependency_links.txt
9
+ mac/ai_usage_bar_mac.egg-info/entry_points.txt
10
+ mac/ai_usage_bar_mac.egg-info/top_level.txt
11
+ mac/aiusagebar/__init__.py
12
+ mac/aiusagebar/__main__.py
13
+ mac/aiusagebar/app.py
14
+ mac/aiusagebar/crypto.py
15
+ mac/aiusagebar/friends.py
16
+ mac/aiusagebar/gui.py
17
+ mac/aiusagebar/jsonc.py
18
+ mac/aiusagebar/logs.py
19
+ mac/aiusagebar/resetwatch.py
20
+ mac/aiusagebar/services.py
21
+ mac/aiusagebar/team.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aiusagebar = aiusagebar.app:main
@@ -0,0 +1,3 @@
1
+ # -*- coding: utf-8 -*-
2
+ """AI Usage Bar — Mac 用チーム参加ツール。"""
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ import sys
3
+ from . import app
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(app.cli(sys.argv[1:]))
@@ -0,0 +1,318 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Mac 用チーム参加ツール本体。
4
+ ・GUI(Tkinter):招待コードを貼り付け → 確認コードを照合 → すべての共有に同意して参加。参加後は自動で報告。
5
+ ・CLI:join / report / status / leave(画面が使えない環境や自動化向け)
6
+ Mac に最初から入っている Python 3(Tkinter 同梱)だけで動く。追加インストール不要。
7
+ """
8
+ import argparse
9
+ import os
10
+ import sys
11
+ import threading
12
+ import time
13
+
14
+ from . import services, team, friends as friendsmod, resetwatch
15
+ from .crypto import TeamError
16
+ from .logs import LocalLogs
17
+
18
+
19
+ REPORT_INTERVAL_SEC = 5 * 60
20
+
21
+
22
+ def _fmt_remaining(v):
23
+ return "—" if v is None else str(int(v)) + "%"
24
+
25
+
26
+ def _print_friends(others, now=None):
27
+ now = now or friendsmod.now_ms()
28
+ if not others:
29
+ print(" (まだフレンドがいません。相手にも同じ共有フォルダで『フレンド』をオンにしてもらってください)")
30
+ return
31
+ for s in others:
32
+ stale = " ※しばらく更新なし" if friendsmod.is_stale(s, now) else ""
33
+ cl = s.get("claudeFiveHour")
34
+ clw = s.get("claudeWeekly")
35
+ cx = "・".join("%s %s" % (w["label"], _fmt_remaining(w["remaining"])) for w in s.get("codex", []))
36
+ parts = []
37
+ if cl is not None or clw is not None:
38
+ parts.append("Claude 5h %s / 週 %s" % (_fmt_remaining(cl), _fmt_remaining(clw)))
39
+ if cx:
40
+ parts.append("Codex " + cx)
41
+ print(" %-16s %s%s" % (s["name"], " ".join(parts) if parts else "残り枠なし", stale))
42
+
43
+
44
+ def _detected_emails():
45
+ out = []
46
+ e = services.claude_email()
47
+ if e:
48
+ out.append(e)
49
+ return out
50
+
51
+
52
+ def _quota():
53
+ q = {"codex": None, "claude": None, "codexServiceDaily": None}
54
+ try:
55
+ snap = services.fetch_codex()
56
+ if snap:
57
+ q["codex"] = services.codex_quota(snap)
58
+ q["codexServiceDaily"] = snap.get("daily")
59
+ except Exception:
60
+ pass
61
+ try:
62
+ c = services.read_claude()
63
+ if c:
64
+ q["claude"] = c
65
+ except Exception:
66
+ pass
67
+ return q
68
+
69
+
70
+ def build_service():
71
+ data_dir = team.default_data_dir()
72
+ logs = LocalLogs()
73
+ logs.scan()
74
+ return team.TeamService(data_dir, logs, _detected_emails, _quota)
75
+
76
+
77
+ def _refresh_logs(svc):
78
+ svc.logs = LocalLogs()
79
+ svc.logs.scan()
80
+
81
+
82
+ # ------------------------------------------------------------------ CLI
83
+
84
+ def cli(argv):
85
+ ap = argparse.ArgumentParser(prog="aiusagebar", description="AI Usage Bar Mac 参加ツール")
86
+ sub = ap.add_subparsers(dest="cmd")
87
+
88
+ j = sub.add_parser("join", help="チームに参加")
89
+ j.add_argument("--invite", help="招待コード、または招待文(ファイルパスでも可)")
90
+ j.add_argument("--folder", help="共有フォルダ(招待コードにフォルダが無い場合)")
91
+ j.add_argument("--fingerprint", help="チーム確認コード(32桁)")
92
+ j.add_argument("--name", default=os.environ.get("USER") or "me")
93
+ j.add_argument("--email", required=True)
94
+ j.add_argument("--yes", action="store_true", help="共有内容に同意して参加")
95
+
96
+ sub.add_parser("report", help="今すぐ報告")
97
+ sub.add_parser("status", help="参加状態を表示")
98
+ sub.add_parser("leave", help="チームから抜ける")
99
+ w = sub.add_parser("watch", help="常駐して定期的に報告(フレンド公開・リセット通知も行う)")
100
+ w.add_argument("--reset", type=int, choices=[0, 1, 2], default=0, help="リセット通知 0:しない 1:残りが少なかったときだけ 2:毎回")
101
+ w.add_argument("--threshold", type=int, default=20, help="「少なかったとき」の残り%%(既定20)")
102
+ fr = sub.add_parser("friends", help="フレンドの残り枠(任意・既定オフ)")
103
+ fr.add_argument("action", nargs="?", choices=["show", "on", "off"], default="show", help="show:一覧 on:公開開始 off:公開停止")
104
+ fr.add_argument("--folder", help="共有フォルダ(この中に AIUsageBar-Friends を作る)")
105
+ fr.add_argument("--invite", help="フレンド招待コード(AIUB-FRIEND-1:… フォルダの場所だけ)")
106
+ fr.add_argument("--name", help="フレンドに見せる表示名")
107
+ sub.add_parser("gui", help="画面を開く")
108
+
109
+ args = ap.parse_args(argv)
110
+ if args.cmd in (None, "gui"):
111
+ from . import gui
112
+ return gui.main()
113
+
114
+ svc = build_service()
115
+
116
+ if args.cmd == "status":
117
+ if not svc.local.joined:
118
+ print("未参加です。 join で参加してください。")
119
+ return 0
120
+ print("チーム:", svc.local.team_id)
121
+ print("共有フォルダ:", svc.local.shared_folder)
122
+ print("あなた:", svc.local.declared_name, "/", ", ".join(svc.my_emails()))
123
+ print("このPCの確認コード:", svc.my_pc_fingerprint())
124
+ print("最終報告:", _fmt_ms(svc.local.last_upload))
125
+ return 0
126
+
127
+ if args.cmd == "join":
128
+ invite_text = args.invite or ""
129
+ if invite_text and os.path.isfile(invite_text):
130
+ invite_text = open(invite_text, encoding="utf-8").read()
131
+ folder = args.folder
132
+ fp = args.fingerprint
133
+ dec = team.decode_invite(invite_text) if invite_text else None
134
+ if dec:
135
+ folder = folder or dec[0]
136
+ fp = fp or dec[2]
137
+ if not folder or not fp:
138
+ print("共有フォルダとチーム確認コードが必要です(--invite か --folder/--fingerprint)", file=sys.stderr)
139
+ return 2
140
+ try:
141
+ conf, resolved = team.peek(folder)
142
+ except TeamError as ex:
143
+ print("読み込めません:", ex, file=sys.stderr)
144
+ return 1
145
+ print("チーム名:", conf.team_name)
146
+ print("チーム確認コード:", conf.fingerprint)
147
+ print("共有される内容:", _describe_full())
148
+ if not args.yes:
149
+ print("\n※ すべて(依頼文の本文を含む)をマスターに共有することに同意する場合は --yes を付けて実行してください。")
150
+ return 1
151
+ try:
152
+ svc.join(folder, fp, args.name, args.email, confirmed=conf)
153
+ except TeamError as ex:
154
+ print("参加できません:", ex, file=sys.stderr)
155
+ return 1
156
+ print("参加しました。最初の報告を送ります…")
157
+ err = svc.upload(force=True)
158
+ print("報告:", "完了" if err is None else err)
159
+ return 0
160
+
161
+ if args.cmd == "friends":
162
+ return _cmd_friends(args)
163
+
164
+ if args.cmd == "watch":
165
+ return _cmd_watch(args, svc)
166
+
167
+ if not svc.local.joined:
168
+ print("未参加です。 join で参加してください。", file=sys.stderr)
169
+ return 1
170
+
171
+ if args.cmd == "report":
172
+ err = svc.upload(force=True)
173
+ print("報告:", "完了" if err is None else err)
174
+ return 0 if err is None else 1
175
+
176
+ if args.cmd == "leave":
177
+ svc.leave()
178
+ print("チームから抜けました。共有フォルダの報告を削除しました。")
179
+ return 0
180
+
181
+ ap.print_help()
182
+ return 0
183
+
184
+
185
+ def _cmd_friends(args):
186
+ data_dir = team.default_data_dir()
187
+ cfg = friendsmod.load_config(data_dir)
188
+ if args.action == "off":
189
+ friendsmod.withdraw(cfg)
190
+ cfg["enabled"] = False
191
+ friendsmod.save_config(data_dir, cfg)
192
+ print("フレンドの公開をやめ、共有フォルダから自分のファイルを消しました。")
193
+ return 0
194
+ if args.action == "on":
195
+ folder = args.folder or (friendsmod.decode_invite(args.invite) if args.invite else None)
196
+ if not folder:
197
+ print("共有フォルダが必要です(--folder または --invite)", file=sys.stderr)
198
+ return 2
199
+ if not os.path.isdir(folder):
200
+ print("共有フォルダが見つかりません:" + folder, file=sys.stderr)
201
+ return 1
202
+ cfg["folder"] = folder
203
+ cfg["enabled"] = True
204
+ if args.name:
205
+ cfg["displayName"] = args.name
206
+ if not cfg.get("displayName"):
207
+ cfg["displayName"] = os.environ.get("USER") or "me"
208
+ friendsmod.save_config(data_dir, cfg)
209
+ snap = _try(services.fetch_codex)
210
+ claude = _try(services.read_claude)
211
+ try:
212
+ friendsmod.publish(cfg, friendsmod.build_mine(cfg, claude, snap))
213
+ except Exception as ex: # noqa: BLE001
214
+ print("公開できませんでした:", ex, file=sys.stderr)
215
+ return 1
216
+ print("フレンドを公開しました(フォルダ:%s/表示名:%s)。" % (folder, cfg["displayName"]))
217
+ print("招待コード(相手に伝える):" + friendsmod.encode_invite(folder))
218
+ print("現在のフレンド:")
219
+ _print_friends(friendsmod.read_others(cfg))
220
+ return 0
221
+ # show
222
+ if not friendsmod.is_configured(cfg):
223
+ print("フレンドはオフです。 friends on --folder <共有フォルダ> でオンにできます。")
224
+ return 0
225
+ print("フレンド(共有フォルダ:%s):" % cfg["folder"])
226
+ _print_friends(friendsmod.read_others(cfg))
227
+ return 0
228
+
229
+
230
+ def _try(fn):
231
+ try:
232
+ return fn()
233
+ except Exception: # noqa: BLE001
234
+ return None
235
+
236
+
237
+ def _cmd_watch(args, svc):
238
+ data_dir = team.default_data_dir()
239
+ fcfg = friendsmod.load_config(data_dir)
240
+ watcher = resetwatch.ResetWatcher()
241
+ last_claude = None
242
+ do_team = svc.local.joined
243
+ do_friends = friendsmod.is_configured(fcfg)
244
+ if not do_team and not do_friends and not args.reset:
245
+ print("参加もフレンドもオフです。 join で参加するか、 friends on でフレンドをオンにしてください。", file=sys.stderr)
246
+ return 1
247
+ msg = "常駐を開始します(Ctrl+C で終了)。"
248
+ if args.reset:
249
+ msg += " リセット通知:" + resetwatch.MODE_NAMES[args.reset]
250
+ print(msg)
251
+ try:
252
+ while True:
253
+ snap = _try(services.fetch_codex) if (do_friends or args.reset) else None
254
+ claude = _try(services.read_claude) if (do_friends or args.reset) else None
255
+ if do_team:
256
+ _refresh_logs(svc)
257
+ err = svc.upload(force=False)
258
+ print(time.strftime("%H:%M"), "報告:", "完了" if err is None else err)
259
+ if do_friends:
260
+ try:
261
+ friendsmod.publish(fcfg, friendsmod.build_mine(fcfg, claude, snap))
262
+ others = friendsmod.read_others(fcfg)
263
+ print(time.strftime("%H:%M"), "フレンド:", len(others), "人")
264
+ except Exception as ex: # noqa: BLE001
265
+ print(time.strftime("%H:%M"), "フレンド: 更新できません(", ex, ")")
266
+ if args.reset:
267
+ for n in watcher.check_codex(snap, args.reset, args.threshold):
268
+ resetwatch.notify(n["title"], n["text"])
269
+ print(" 通知:", n["title"])
270
+ if last_claude is not None:
271
+ for n in watcher.check_claude(last_claude, claude, args.reset, args.threshold):
272
+ resetwatch.notify(n["title"], n["text"])
273
+ print(" 通知:", n["title"])
274
+ if claude is not None:
275
+ last_claude = claude
276
+ time.sleep(REPORT_INTERVAL_SEC)
277
+ except KeyboardInterrupt:
278
+ print("\n終了します。")
279
+ return 0
280
+
281
+
282
+ def _fmt_ms(ms):
283
+ if not ms:
284
+ return "まだありません"
285
+ return time.strftime("%Y-%m-%d %H:%M", time.localtime(ms / 1000))
286
+
287
+
288
+ def _describe_full():
289
+ return ("PC名・ユーザー名・メールアドレス、利用量(トークン数・残り枠・日別・モデル別)、"
290
+ "セッションのタイトル・作業フォルダ・時間帯、Claude Code・Codex に入力した依頼文の本文(直近14日)")
291
+
292
+
293
+ def start_background_reporter(svc, on_status=None):
294
+ """GUI から使う:別スレッドで定期的に報告する。"""
295
+ stop = threading.Event()
296
+
297
+ def loop():
298
+ first = True
299
+ while not stop.is_set():
300
+ try:
301
+ _refresh_logs(svc)
302
+ err = svc.upload(force=first)
303
+ if on_status:
304
+ on_status(err)
305
+ except Exception as ex: # noqa: BLE001
306
+ if on_status:
307
+ on_status(str(ex))
308
+ first = False
309
+ stop.wait(REPORT_INTERVAL_SEC)
310
+
311
+ th = threading.Thread(target=loop, daemon=True)
312
+ th.start()
313
+ return stop
314
+
315
+
316
+ def main():
317
+ """Installed console entry point; preserve the existing CLI behavior."""
318
+ return cli(sys.argv[1:])