tokentray 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.
Files changed (140) hide show
  1. tokentray-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +79 -0
  2. tokentray-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. tokentray-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +20 -0
  4. tokentray-0.1.0/.github/workflows/ci.yml +59 -0
  5. tokentray-0.1.0/.github/workflows/release.yml +132 -0
  6. tokentray-0.1.0/.github/workflows/tests.yml +41 -0
  7. tokentray-0.1.0/.gitignore +22 -0
  8. tokentray-0.1.0/.python-version +1 -0
  9. tokentray-0.1.0/CHANGELOG.md +92 -0
  10. tokentray-0.1.0/CONTRIBUTING.md +67 -0
  11. tokentray-0.1.0/LICENSE +22 -0
  12. tokentray-0.1.0/MACOS_VALIDATION.2026-09-10.ko.md +546 -0
  13. tokentray-0.1.0/MANUAL_TEST.ko.md +135 -0
  14. tokentray-0.1.0/MANUAL_TEST.md +140 -0
  15. tokentray-0.1.0/PKG-INFO +361 -0
  16. tokentray-0.1.0/README.ko.md +315 -0
  17. tokentray-0.1.0/README.md +328 -0
  18. tokentray-0.1.0/SECURITY.md +38 -0
  19. tokentray-0.1.0/docs/images/hero-en-dark.png +0 -0
  20. tokentray-0.1.0/docs/images/hero-en-light.png +0 -0
  21. tokentray-0.1.0/docs/images/hero-ko-dark.png +0 -0
  22. tokentray-0.1.0/docs/images/hero-ko-light.png +0 -0
  23. tokentray-0.1.0/docs/images/integrations-en-dark.png +0 -0
  24. tokentray-0.1.0/docs/images/integrations-en-light.png +0 -0
  25. tokentray-0.1.0/docs/images/integrations-ko-dark.png +0 -0
  26. tokentray-0.1.0/docs/images/integrations-ko-light.png +0 -0
  27. tokentray-0.1.0/docs/images/login-recovery-en-dark.png +0 -0
  28. tokentray-0.1.0/docs/images/login-recovery-en-light.png +0 -0
  29. tokentray-0.1.0/docs/images/login-recovery-ko-dark.png +0 -0
  30. tokentray-0.1.0/docs/images/login-recovery-ko-light.png +0 -0
  31. tokentray-0.1.0/packaging/entry_cli.py +6 -0
  32. tokentray-0.1.0/packaging/entry_gui.py +8 -0
  33. tokentray-0.1.0/packaging/linux/install.sh +67 -0
  34. tokentray-0.1.0/packaging/linux/io.github.messy-snail.tokentray.desktop +11 -0
  35. tokentray-0.1.0/packaging/make_icons.py +108 -0
  36. tokentray-0.1.0/packaging/resources/tokentray-128.png +0 -0
  37. tokentray-0.1.0/packaging/resources/tokentray-16.png +0 -0
  38. tokentray-0.1.0/packaging/resources/tokentray-24.png +0 -0
  39. tokentray-0.1.0/packaging/resources/tokentray-256.png +0 -0
  40. tokentray-0.1.0/packaging/resources/tokentray-32.png +0 -0
  41. tokentray-0.1.0/packaging/resources/tokentray-48.png +0 -0
  42. tokentray-0.1.0/packaging/resources/tokentray-512.png +0 -0
  43. tokentray-0.1.0/packaging/resources/tokentray-64.png +0 -0
  44. tokentray-0.1.0/packaging/resources/tokentray.icns +0 -0
  45. tokentray-0.1.0/packaging/resources/tokentray.ico +0 -0
  46. tokentray-0.1.0/packaging/smoke.sh +76 -0
  47. tokentray-0.1.0/packaging/tokentray.spec +169 -0
  48. tokentray-0.1.0/pyproject.toml +90 -0
  49. tokentray-0.1.0/scripts/check_release_version.py +27 -0
  50. tokentray-0.1.0/scripts/render_screenshots.py +305 -0
  51. tokentray-0.1.0/scripts/verify-linux.sh +165 -0
  52. tokentray-0.1.0/src/tokentray/__init__.py +3 -0
  53. tokentray-0.1.0/src/tokentray/__main__.py +6 -0
  54. tokentray-0.1.0/src/tokentray/app.py +491 -0
  55. tokentray-0.1.0/src/tokentray/autostart/__init__.py +100 -0
  56. tokentray-0.1.0/src/tokentray/autostart/linux.py +69 -0
  57. tokentray-0.1.0/src/tokentray/autostart/macos.py +71 -0
  58. tokentray-0.1.0/src/tokentray/autostart/windows.py +80 -0
  59. tokentray-0.1.0/src/tokentray/bootstrap.py +71 -0
  60. tokentray-0.1.0/src/tokentray/cli.py +467 -0
  61. tokentray-0.1.0/src/tokentray/connections.py +162 -0
  62. tokentray-0.1.0/src/tokentray/core/__init__.py +1 -0
  63. tokentray-0.1.0/src/tokentray/core/alerts.py +293 -0
  64. tokentray-0.1.0/src/tokentray/core/cache.py +111 -0
  65. tokentray-0.1.0/src/tokentray/core/compute.py +223 -0
  66. tokentray-0.1.0/src/tokentray/core/config.py +196 -0
  67. tokentray-0.1.0/src/tokentray/core/connection_strings.py +67 -0
  68. tokentray-0.1.0/src/tokentray/core/i18n.py +304 -0
  69. tokentray-0.1.0/src/tokentray/core/models.py +93 -0
  70. tokentray-0.1.0/src/tokentray/core/paths.py +127 -0
  71. tokentray-0.1.0/src/tokentray/core/secrets.py +153 -0
  72. tokentray-0.1.0/src/tokentray/core/state.py +41 -0
  73. tokentray-0.1.0/src/tokentray/core/view.py +305 -0
  74. tokentray-0.1.0/src/tokentray/diagnostics.py +257 -0
  75. tokentray-0.1.0/src/tokentray/ipc.py +228 -0
  76. tokentray-0.1.0/src/tokentray/login_recovery.py +215 -0
  77. tokentray-0.1.0/src/tokentray/login_terminal.py +73 -0
  78. tokentray-0.1.0/src/tokentray/notify/__init__.py +1 -0
  79. tokentray-0.1.0/src/tokentray/notify/configuration.py +94 -0
  80. tokentray-0.1.0/src/tokentray/notify/dispatcher.py +100 -0
  81. tokentray-0.1.0/src/tokentray/notify/formatting.py +73 -0
  82. tokentray-0.1.0/src/tokentray/notify/webhook.py +305 -0
  83. tokentray-0.1.0/src/tokentray/polling.py +84 -0
  84. tokentray-0.1.0/src/tokentray/providers/__init__.py +27 -0
  85. tokentray-0.1.0/src/tokentray/providers/base.py +186 -0
  86. tokentray-0.1.0/src/tokentray/providers/claude.py +261 -0
  87. tokentray-0.1.0/src/tokentray/providers/codex.py +328 -0
  88. tokentray-0.1.0/src/tokentray/resources/fonts/OFL.txt +94 -0
  89. tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-Bold.otf +0 -0
  90. tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-Regular.otf +0 -0
  91. tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-SemiBold.otf +0 -0
  92. tokentray-0.1.0/src/tokentray/resources/providers/OpenAI-black-monoblossom.svg +15 -0
  93. tokentray-0.1.0/src/tokentray/resources/providers/OpenAI-white-monoblossom.svg +15 -0
  94. tokentray-0.1.0/src/tokentray/resources/providers/SOURCES.md +27 -0
  95. tokentray-0.1.0/src/tokentray/resources/providers/claude.svg +1 -0
  96. tokentray-0.1.0/src/tokentray/resources/providers/simple-icons-LICENSE.md +30 -0
  97. tokentray-0.1.0/src/tokentray/setup_display.py +52 -0
  98. tokentray-0.1.0/src/tokentray/setup_wizard.py +190 -0
  99. tokentray-0.1.0/src/tokentray/ui/__init__.py +1 -0
  100. tokentray-0.1.0/src/tokentray/ui/elided.py +29 -0
  101. tokentray-0.1.0/src/tokentray/ui/fonts.py +26 -0
  102. tokentray-0.1.0/src/tokentray/ui/icons.py +200 -0
  103. tokentray-0.1.0/src/tokentray/ui/integration.py +326 -0
  104. tokentray-0.1.0/src/tokentray/ui/loading_overlay.py +68 -0
  105. tokentray-0.1.0/src/tokentray/ui/panel.py +336 -0
  106. tokentray-0.1.0/src/tokentray/ui/popup.py +487 -0
  107. tokentray-0.1.0/src/tokentray/ui/provider_icons.py +59 -0
  108. tokentray-0.1.0/src/tokentray/ui/refresh_feedback.py +71 -0
  109. tokentray-0.1.0/src/tokentray/ui/theme.py +126 -0
  110. tokentray-0.1.0/src/tokentray/ui/tray.py +235 -0
  111. tokentray-0.1.0/src/tokentray/ui/wrapping.py +28 -0
  112. tokentray-0.1.0/tests/conftest.py +129 -0
  113. tokentray-0.1.0/tests/manual_connection_ui.py +126 -0
  114. tokentray-0.1.0/tests/test_alerts.py +235 -0
  115. tokentray-0.1.0/tests/test_cli_and_notify.py +391 -0
  116. tokentray-0.1.0/tests/test_compute.py +162 -0
  117. tokentray-0.1.0/tests/test_connection_ui.py +112 -0
  118. tokentray-0.1.0/tests/test_connections.py +174 -0
  119. tokentray-0.1.0/tests/test_core.py +282 -0
  120. tokentray-0.1.0/tests/test_integration_layout.py +197 -0
  121. tokentray-0.1.0/tests/test_ipc_buffered.py +27 -0
  122. tokentray-0.1.0/tests/test_ipc_reply_read.py +56 -0
  123. tokentray-0.1.0/tests/test_keychain_refusal.py +157 -0
  124. tokentray-0.1.0/tests/test_language_switch.py +129 -0
  125. tokentray-0.1.0/tests/test_linux_lifecycle.py +59 -0
  126. tokentray-0.1.0/tests/test_login_recovery.py +183 -0
  127. tokentray-0.1.0/tests/test_notification_preview.py +258 -0
  128. tokentray-0.1.0/tests/test_notification_routing.py +146 -0
  129. tokentray-0.1.0/tests/test_notifications.py +257 -0
  130. tokentray-0.1.0/tests/test_panel_layout.py +172 -0
  131. tokentray-0.1.0/tests/test_platform.py +619 -0
  132. tokentray-0.1.0/tests/test_provider_identity.py +125 -0
  133. tokentray-0.1.0/tests/test_providers.py +468 -0
  134. tokentray-0.1.0/tests/test_refresh_feedback.py +149 -0
  135. tokentray-0.1.0/tests/test_release_version.py +25 -0
  136. tokentray-0.1.0/tests/test_screenshots.py +44 -0
  137. tokentray-0.1.0/tests/test_setup_connections.py +134 -0
  138. tokentray-0.1.0/tests/test_state_reset.py +200 -0
  139. tokentray-0.1.0/tests/test_ui.py +680 -0
  140. tokentray-0.1.0/uv.lock +702 -0
@@ -0,0 +1,79 @@
1
+ name: Bug report
2
+ description: Something in tokentray does not work as described. 한국어로 작성해도 됩니다.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting. Please **never paste login tokens, webhook URLs or your
9
+ email address**. `tokentray doctor` prints no tokens, but its file paths can
10
+ contain your user name - edit those out if that matters to you.
11
+ Security problems belong in a private report instead: see SECURITY.md.
12
+ - type: input
13
+ id: version
14
+ attributes:
15
+ label: tokentray version
16
+ description: Output of `tokentray --version`.
17
+ placeholder: tokentray 0.1.0
18
+ validations:
19
+ required: true
20
+ - type: dropdown
21
+ id: install
22
+ attributes:
23
+ label: How did you install it?
24
+ options:
25
+ - Release download (zip / tar.gz)
26
+ - uv tool install tokentray
27
+ - From source (git clone)
28
+ - Other
29
+ validations:
30
+ required: true
31
+ - type: input
32
+ id: os
33
+ attributes:
34
+ label: Operating system
35
+ description: Include the version, and on Linux the desktop and session type (`echo $XDG_SESSION_TYPE`).
36
+ placeholder: Ubuntu 24.04, GNOME, Wayland / Windows 11 24H2 / macOS 15.6
37
+ validations:
38
+ required: true
39
+ - type: checkboxes
40
+ id: services
41
+ attributes:
42
+ label: Which services are signed in?
43
+ options:
44
+ - label: Claude Code
45
+ - label: Codex
46
+ - type: textarea
47
+ id: what-happened
48
+ attributes:
49
+ label: What happened, and what did you expect?
50
+ description: Screenshots help, especially for tray, panel or notification problems.
51
+ validations:
52
+ required: true
53
+ - type: textarea
54
+ id: steps
55
+ attributes:
56
+ label: Steps to reproduce
57
+ placeholder: |
58
+ 1. Start tokentray
59
+ 2. Click the tray icon
60
+ 3. ...
61
+ - type: textarea
62
+ id: doctor
63
+ attributes:
64
+ label: Output of `tokentray doctor`
65
+ description: On macOS packaged builds the CLI is at `/Applications/tokentray.app/Contents/MacOS/tokentray`.
66
+ render: text
67
+ - type: textarea
68
+ id: log
69
+ attributes:
70
+ label: Relevant log lines
71
+ description: Tray menu → **Open log**. The lines around the time of the problem are enough.
72
+ render: text
73
+ - type: checkboxes
74
+ id: redacted
75
+ attributes:
76
+ label: Before submitting
77
+ options:
78
+ - label: I removed tokens, webhook URLs and email addresses from everything above.
79
+ required: true
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Report a security vulnerability
4
+ url: https://github.com/messy-snail/tokentray/security/advisories/new
5
+ about: Token handling, keyring storage or webhook problems - please report privately, not as a public issue.
@@ -0,0 +1,20 @@
1
+ name: Feature request
2
+ description: Suggest something tokentray should show or do. 한국어로 작성해도 됩니다.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What are you trying to do?
9
+ description: Describe the situation first; a proposed solution is welcome but optional.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: What would help?
16
+ - type: input
17
+ id: os
18
+ attributes:
19
+ label: Operating system
20
+ placeholder: Windows 11 / macOS 15 / Ubuntu 24.04
@@ -0,0 +1,59 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint:
14
+ # Style only, and deliberately not a gate on release.yml: a tag should not
15
+ # be blocked by an import order, and the suite is what proves behaviour.
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v5
20
+ with:
21
+ enable-cache: true
22
+ - run: uv run ruff check .
23
+
24
+ test:
25
+ uses: ./.github/workflows/tests.yml
26
+
27
+ bundle:
28
+ # The packaged build has its own failure modes - a spec that picks the wrong
29
+ # executable, a missing icon, an entry point that never returns - and none of
30
+ # them show up in the unit suite. Building here means a tag is not the first
31
+ # time anyone finds out.
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ os: [ubuntu-latest, windows-latest, macos-14]
36
+ runs-on: ${{ matrix.os }}
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - name: Install Qt runtime libraries
41
+ if: runner.os == 'Linux'
42
+ run: |
43
+ sudo apt-get update
44
+ sudo apt-get install -y --no-install-recommends \
45
+ libegl1 libgl1 libxkbcommon0 libdbus-1-3 libfontconfig1 libglib2.0-0 \
46
+ libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libxcb-xkb1
47
+
48
+ - uses: astral-sh/setup-uv@v5
49
+ with:
50
+ enable-cache: true
51
+
52
+ - name: Build
53
+ run: uv run pyinstaller --noconfirm --distpath dist --workpath build packaging/tokentray.spec
54
+
55
+ - name: Smoke test the bundle
56
+ shell: bash
57
+ env:
58
+ QT_QPA_PLATFORM: offscreen
59
+ run: ./packaging/smoke.sh
@@ -0,0 +1,132 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ validate:
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: read
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v5
19
+ with:
20
+ python-version: "3.12"
21
+ - name: Check tag against package version
22
+ run: uv run --no-project python scripts/check_release_version.py
23
+
24
+ test:
25
+ needs: validate
26
+ uses: ./.github/workflows/tests.yml
27
+ permissions:
28
+ contents: read
29
+
30
+ bundle:
31
+ needs: test
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ include:
36
+ # Built inside a 22.04 container so the glibc requirement stays low
37
+ # enough for older distributions; a 24.04 runner would not run there.
38
+ - os: ubuntu-latest
39
+ container: ubuntu:22.04
40
+ asset: tokentray-linux-x86_64
41
+ - os: windows-latest
42
+ asset: tokentray-windows-x86_64
43
+ - os: macos-14
44
+ asset: tokentray-macos-arm64
45
+ runs-on: ${{ matrix.os }}
46
+ container: ${{ matrix.container }}
47
+ steps:
48
+ - name: Install container prerequisites
49
+ if: matrix.container
50
+ run: |
51
+ apt-get update
52
+ apt-get install -y --no-install-recommends \
53
+ ca-certificates curl git binutils \
54
+ libegl1 libgl1 libxkbcommon0 libdbus-1-3 libfontconfig1 libglib2.0-0 \
55
+ libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libxcb-xkb1
56
+
57
+ - uses: actions/checkout@v4
58
+
59
+ - uses: astral-sh/setup-uv@v5
60
+ with:
61
+ enable-cache: true
62
+
63
+ - name: Build
64
+ run: uv run pyinstaller --noconfirm --distpath dist --workpath build packaging/tokentray.spec
65
+
66
+ - name: Smoke test the bundle
67
+ env:
68
+ QT_QPA_PLATFORM: offscreen
69
+ shell: bash
70
+ # Same script CI runs on every pull request, so a release cannot be the
71
+ # first build anyone actually checks.
72
+ run: ./packaging/smoke.sh
73
+
74
+ - name: Sign ad-hoc (macOS)
75
+ if: runner.os == 'macOS'
76
+ # Unsigned, unnotarised: Gatekeeper still needs the quarantine flag
77
+ # cleared by hand. The README says so; ad-hoc signing just keeps the
78
+ # bundle from being rejected outright.
79
+ run: codesign --force --deep --sign - dist/tokentray.app || true
80
+
81
+ - name: Archive
82
+ shell: bash
83
+ run: |
84
+ if [ "$RUNNER_OS" = "Windows" ]; then
85
+ cd dist && 7z a -tzip "../${{ matrix.asset }}.zip" tokentray
86
+ elif [ "$RUNNER_OS" = "macOS" ]; then
87
+ cd dist && zip -qry "../${{ matrix.asset }}.zip" tokentray.app
88
+ else
89
+ # Linux gets the desktop integration alongside the binaries: there is
90
+ # no package manager in a tarball, so install.sh is how the app gets
91
+ # a launcher entry and an icon.
92
+ cp packaging/linux/install.sh packaging/linux/*.desktop dist/
93
+ mkdir -p dist/resources
94
+ cp packaging/resources/tokentray-*.png dist/resources/
95
+ cd dist && tar czf "../${{ matrix.asset }}.tar.gz" \
96
+ tokentray install.sh io.github.messy-snail.tokentray.desktop resources
97
+ fi
98
+
99
+ - uses: actions/upload-artifact@v4
100
+ with:
101
+ name: ${{ matrix.asset }}
102
+ path: |
103
+ ${{ matrix.asset }}.zip
104
+ ${{ matrix.asset }}.tar.gz
105
+ if-no-files-found: error
106
+
107
+ release:
108
+ needs: bundle
109
+ runs-on: ubuntu-latest
110
+ if: startsWith(github.ref, 'refs/tags/v')
111
+ steps:
112
+ - uses: actions/download-artifact@v4
113
+ with:
114
+ path: artifacts
115
+ merge-multiple: true
116
+ - uses: softprops/action-gh-release@v2
117
+ with:
118
+ files: artifacts/*
119
+ generate_release_notes: true
120
+
121
+ pypi:
122
+ needs: bundle
123
+ runs-on: ubuntu-latest
124
+ if: startsWith(github.ref, 'refs/tags/v')
125
+ environment: pypi
126
+ permissions:
127
+ id-token: write # trusted publishing; no API token stored in the repo
128
+ steps:
129
+ - uses: actions/checkout@v4
130
+ - uses: astral-sh/setup-uv@v5
131
+ - run: uv build
132
+ - run: uv publish
@@ -0,0 +1,41 @@
1
+ name: Tests
2
+
3
+ on:
4
+ workflow_call:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ubuntu-latest, windows-latest, macos-14]
15
+ # 3.12 is what .python-version pins, so it is what a developer's
16
+ # `uv run` actually uses; 3.11 and 3.13 are the supported edges.
17
+ python: ["3.11", "3.12", "3.13"]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Install Qt runtime libraries
23
+ if: runner.os == 'Linux'
24
+ # PySide6 links these even when running headless under the offscreen
25
+ # platform plugin; without them `import PySide6.QtWidgets` fails. Kept
26
+ # in step with the release workflow's list.
27
+ run: |
28
+ sudo apt-get update
29
+ sudo apt-get install -y --no-install-recommends \
30
+ libegl1 libgl1 libxkbcommon0 libdbus-1-3 libfontconfig1 libglib2.0-0 \
31
+ libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libxcb-xkb1
32
+
33
+ - uses: astral-sh/setup-uv@v5
34
+ with:
35
+ enable-cache: true
36
+
37
+ - name: Run tests
38
+ env:
39
+ QT_QPA_PLATFORM: offscreen
40
+ run: uv run --python ${{ matrix.python }} pytest -q
41
+
@@ -0,0 +1,22 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # packaging
13
+ /build/
14
+ /dist/
15
+ *.spec.bak
16
+
17
+ # Claude Code scratch. A git worktree must never be committed into its own repo.
18
+ .claude/worktrees/
19
+
20
+ # scripts/verify-linux.sh writes its log into the repo root; the tree has to be
21
+ # clean at tag time.
22
+ verify-linux-*.log
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,92 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-09-14
4
+
5
+ First release. A cross-platform reimplementation of
6
+ [haomingkoo/claude-codex-monitor](https://github.com/haomingkoo/claude-codex-monitor)
7
+ (MIT), whose macOS and Windows scripts had drifted apart; here both platforms
8
+ run the same code.
9
+
10
+ ### Added
11
+ - Tray icon showing Claude Code and Codex remaining quota as concentric rings.
12
+ - Self-drawn toast notifications at 50/25/10% remaining, plus reset reminders.
13
+ - Detail panel with per-window meters, reset countdown, pace and burnout.
14
+ - `status`, `setup`, `doctor`, `open`, `refresh`, `stop`, `config`, `autostart`.
15
+ - Start-at-login on Windows, macOS and Linux, registering the GUI entry point.
16
+ - Single-instance enforcement with a local command socket.
17
+ - English and Korean, chosen from the desktop locale on first run.
18
+ - Optional Slack, Discord, ntfy or generic webhook delivery, configurable from
19
+ the tray with URLs kept in the OS keyring.
20
+ - Provider-labelled notifications with the tokentray ring icon, so Claude Code
21
+ and Codex alerts can be identified at a glance.
22
+ - `state reset` for re-arming the first-run notice and the alert memory, applied
23
+ through the running instance so its in-memory copy cannot overwrite it.
24
+ - `doctor` reports whether OS notification delivery stands any chance here: the
25
+ `.app` bundle, its signature, and what that means on macOS.
26
+ - Every OS notification attempt is logged, so a channel that drops them silently
27
+ leaves evidence instead of nothing.
28
+ - "Test notification" now fires the OS notification centre alongside the in-app
29
+ toast, so a dead native channel can be told from one nobody called.
30
+ - `config set` hands the change to a running instance, so thresholds, reminders,
31
+ popup position and duration, and `native_notifications` no longer wait for a
32
+ restart.
33
+ - `popup.position` now works. It was listed in the defaults, written into every
34
+ config file and read by nothing; `auto`, `bottom-right` and `top-right` are
35
+ honoured, and the `off` it used to offer is gone - it never did anything, so a
36
+ config still carrying it keeps behaving as before.
37
+ - Launching `tokentray` while it is already running now says so, with the
38
+ running pid and, on Windows, where hidden tray icons live. It used to exit
39
+ without a word, which reads exactly like a failed start. `doctor` reports the
40
+ running instance too.
41
+ - The tray tooltip always leads with `tokentray`, so the entry can be found in
42
+ Windows' "Other system tray icons" settings, where it is otherwise a python.exe.
43
+ - On Windows the tray icon follows the taskbar's colour mode instead of the apps'
44
+ mode, so light apps on a dark taskbar no longer paint a ring that all but vanishes.
45
+
46
+ ### Fixed
47
+ - The self-drawn card stayed hidden on macOS whenever another app was frontmost.
48
+ A Qt::Tool window is an NSPanel that the OS hides on deactivation - exactly
49
+ when an alert is worth seeing, and outside an app bundle macOS delivers no
50
+ banner, so the card may be the only channel.
51
+ - Switching language left the detail panel half-translated until the next poll.
52
+ The views carry already-translated text, so the switch now rebuilds them from
53
+ the last snapshots instead of re-rendering what was already worded.
54
+ - Stacked cards overlapped by the height of a menu bar. The top slot asked for a
55
+ position above the work area - the transparent shadow margin is wider than the
56
+ edge inset - and macOS answered by sliding that window back down, while the
57
+ cards below were still spaced from the position nothing ever had.
58
+ - Hovering a card no longer let it expire under the cursor. Qt reports Leave when
59
+ the pointer crosses onto a child widget, so settling on the card body counted
60
+ as leaving and restarted the eight seconds.
61
+ - On Windows, `status`, `config set` and a second launch could now and then fail
62
+ to hear back from the running app. The app hung up right after answering, and
63
+ closing the server end of a named pipe throws away bytes the client has not
64
+ read yet; it now waits for the client to hang up first.
65
+ - "Refresh now" reported failure whenever one service was not signed in, so
66
+ anyone using only Claude Code or only Codex saw "Refresh failed" on every
67
+ click. A service nobody signed in to no longer counts against the result.
68
+ - Closing "Notification integrations…" left its menu item dead until restart.
69
+ The dialog deletes itself on close; the controller kept the stale wrapper, so
70
+ the next open raised inside the slot and silently did nothing.
71
+ - A refused Claude Code keychain read was retried on every poll and on every
72
+ two-second login probe, each call able to bring the authorization prompt back,
73
+ and it was shown as "not logged in". It now says the keychain could not be
74
+ read and asks again only when you refresh or press Check again. At launch the
75
+ first poll and the login probe also read it at the same moment, so a denied
76
+ keychain could ask twice before either saw the refusal; those reads now wait
77
+ for each other.
78
+
79
+ ### Differences from the reference implementation
80
+ - Alerts cover every window, including Codex and the per-model sub-limits;
81
+ upstream alerted only on Claude's two windows.
82
+ - A sharp drop reports the worst threshold crossed instead of stepping down one
83
+ level per poll, which delayed an "almost out" warning by several minutes.
84
+ - "Still working?" is measured as a usage delta between polls. Upstream compared
85
+ pace against 1.3x, which its own 60/30/10-minute reminder defaults can never
86
+ satisfy - exceeding 1.3x needs at least 23% of the window still to run.
87
+ - Backoff is stored as an explicit timestamp rather than by touching the cache
88
+ file's mtime, so a suppressed failure is distinguishable from a fresh success.
89
+ - Claude's OAuth token is never refreshed by tokentray; Codex refresh is opt-in
90
+ and re-checks the file before writing so it cannot clobber the Codex CLI.
91
+ - A missing seven-day window no longer blanks the display, and Codex window
92
+ labels follow the API-reported duration.
@@ -0,0 +1,67 @@
1
+ # Contributing to tokentray
2
+
3
+ Thanks for helping out. Issues and pull requests are welcome in English or
4
+ Korean (한국어로 작성해도 됩니다).
5
+
6
+ ## Bugs, ideas and security reports
7
+
8
+ - **Bug** - open a
9
+ [bug report](https://github.com/messy-snail/tokentray/issues/new?template=bug_report.yml).
10
+ Include `tokentray --version`, your operating system and the output of
11
+ `tokentray doctor`. Remove tokens, webhook URLs and email addresses first.
12
+ - **Idea** - open a
13
+ [feature request](https://github.com/messy-snail/tokentray/issues/new?template=feature_request.yml)
14
+ that describes what you are trying to do.
15
+ - **Security problem** - please do not open a public issue; follow
16
+ [SECURITY.md](SECURITY.md).
17
+
18
+ ## Development setup
19
+
20
+ You need [uv](https://docs.astral.sh/uv/); it fetches a suitable Python.
21
+
22
+ ```bash
23
+ git clone https://github.com/messy-snail/tokentray
24
+ cd tokentray
25
+ uv sync
26
+ uv run tokentray # run the tray app from the checkout
27
+ uv run pytest # widget tests use Qt's offscreen platform
28
+ uv run ruff check .
29
+ ```
30
+
31
+ ## Pull requests
32
+
33
+ 1. Branch from `dev` and open the pull request against `dev`. Releases are cut
34
+ from `main`.
35
+ 2. Add or update tests when you change behaviour.
36
+ 3. If you change something users see, update both `README.md` and
37
+ `README.ko.md`. A short English note is fine; the maintainer can fill in the
38
+ Korean.
39
+ 4. If you change the panel, alert cards, tray icon or integrations window,
40
+ regenerate the README screenshots:
41
+
42
+ ```bash
43
+ uv run python scripts/render_screenshots.py
44
+ ```
45
+
46
+ 5. Write commit messages as [Conventional Commits](https://www.conventionalcommits.org/),
47
+ for example `fix(ui): keep the panel inside the screen`. English is
48
+ preferred; Korean is fine too.
49
+
50
+ ## Packaging and desktop checks
51
+
52
+ Build and smoke-test a bundle the same way CI and the release workflow do:
53
+
54
+ ```bash
55
+ uv run pyinstaller --noconfirm --distpath dist --workpath build packaging/tokentray.spec
56
+ ./packaging/smoke.sh
57
+ ```
58
+
59
+ After changing the icon design, regenerate and commit the icon files:
60
+
61
+ ```bash
62
+ QT_QPA_PLATFORM=offscreen uv run python packaging/make_icons.py
63
+ ```
64
+
65
+ Automated tests cannot see a real desktop. [MANUAL_TEST.md](MANUAL_TEST.md)
66
+ lists what to check by hand, and on Linux `./scripts/verify-linux.sh` runs every
67
+ desktop check that does not need a person watching.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 koohaoming
4
+ Copyright (c) 2026 hskang
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.