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.
- tokentray-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +79 -0
- tokentray-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- tokentray-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +20 -0
- tokentray-0.1.0/.github/workflows/ci.yml +59 -0
- tokentray-0.1.0/.github/workflows/release.yml +132 -0
- tokentray-0.1.0/.github/workflows/tests.yml +41 -0
- tokentray-0.1.0/.gitignore +22 -0
- tokentray-0.1.0/.python-version +1 -0
- tokentray-0.1.0/CHANGELOG.md +92 -0
- tokentray-0.1.0/CONTRIBUTING.md +67 -0
- tokentray-0.1.0/LICENSE +22 -0
- tokentray-0.1.0/MACOS_VALIDATION.2026-09-10.ko.md +546 -0
- tokentray-0.1.0/MANUAL_TEST.ko.md +135 -0
- tokentray-0.1.0/MANUAL_TEST.md +140 -0
- tokentray-0.1.0/PKG-INFO +361 -0
- tokentray-0.1.0/README.ko.md +315 -0
- tokentray-0.1.0/README.md +328 -0
- tokentray-0.1.0/SECURITY.md +38 -0
- tokentray-0.1.0/docs/images/hero-en-dark.png +0 -0
- tokentray-0.1.0/docs/images/hero-en-light.png +0 -0
- tokentray-0.1.0/docs/images/hero-ko-dark.png +0 -0
- tokentray-0.1.0/docs/images/hero-ko-light.png +0 -0
- tokentray-0.1.0/docs/images/integrations-en-dark.png +0 -0
- tokentray-0.1.0/docs/images/integrations-en-light.png +0 -0
- tokentray-0.1.0/docs/images/integrations-ko-dark.png +0 -0
- tokentray-0.1.0/docs/images/integrations-ko-light.png +0 -0
- tokentray-0.1.0/docs/images/login-recovery-en-dark.png +0 -0
- tokentray-0.1.0/docs/images/login-recovery-en-light.png +0 -0
- tokentray-0.1.0/docs/images/login-recovery-ko-dark.png +0 -0
- tokentray-0.1.0/docs/images/login-recovery-ko-light.png +0 -0
- tokentray-0.1.0/packaging/entry_cli.py +6 -0
- tokentray-0.1.0/packaging/entry_gui.py +8 -0
- tokentray-0.1.0/packaging/linux/install.sh +67 -0
- tokentray-0.1.0/packaging/linux/io.github.messy-snail.tokentray.desktop +11 -0
- tokentray-0.1.0/packaging/make_icons.py +108 -0
- tokentray-0.1.0/packaging/resources/tokentray-128.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-16.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-24.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-256.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-32.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-48.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-512.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray-64.png +0 -0
- tokentray-0.1.0/packaging/resources/tokentray.icns +0 -0
- tokentray-0.1.0/packaging/resources/tokentray.ico +0 -0
- tokentray-0.1.0/packaging/smoke.sh +76 -0
- tokentray-0.1.0/packaging/tokentray.spec +169 -0
- tokentray-0.1.0/pyproject.toml +90 -0
- tokentray-0.1.0/scripts/check_release_version.py +27 -0
- tokentray-0.1.0/scripts/render_screenshots.py +305 -0
- tokentray-0.1.0/scripts/verify-linux.sh +165 -0
- tokentray-0.1.0/src/tokentray/__init__.py +3 -0
- tokentray-0.1.0/src/tokentray/__main__.py +6 -0
- tokentray-0.1.0/src/tokentray/app.py +491 -0
- tokentray-0.1.0/src/tokentray/autostart/__init__.py +100 -0
- tokentray-0.1.0/src/tokentray/autostart/linux.py +69 -0
- tokentray-0.1.0/src/tokentray/autostart/macos.py +71 -0
- tokentray-0.1.0/src/tokentray/autostart/windows.py +80 -0
- tokentray-0.1.0/src/tokentray/bootstrap.py +71 -0
- tokentray-0.1.0/src/tokentray/cli.py +467 -0
- tokentray-0.1.0/src/tokentray/connections.py +162 -0
- tokentray-0.1.0/src/tokentray/core/__init__.py +1 -0
- tokentray-0.1.0/src/tokentray/core/alerts.py +293 -0
- tokentray-0.1.0/src/tokentray/core/cache.py +111 -0
- tokentray-0.1.0/src/tokentray/core/compute.py +223 -0
- tokentray-0.1.0/src/tokentray/core/config.py +196 -0
- tokentray-0.1.0/src/tokentray/core/connection_strings.py +67 -0
- tokentray-0.1.0/src/tokentray/core/i18n.py +304 -0
- tokentray-0.1.0/src/tokentray/core/models.py +93 -0
- tokentray-0.1.0/src/tokentray/core/paths.py +127 -0
- tokentray-0.1.0/src/tokentray/core/secrets.py +153 -0
- tokentray-0.1.0/src/tokentray/core/state.py +41 -0
- tokentray-0.1.0/src/tokentray/core/view.py +305 -0
- tokentray-0.1.0/src/tokentray/diagnostics.py +257 -0
- tokentray-0.1.0/src/tokentray/ipc.py +228 -0
- tokentray-0.1.0/src/tokentray/login_recovery.py +215 -0
- tokentray-0.1.0/src/tokentray/login_terminal.py +73 -0
- tokentray-0.1.0/src/tokentray/notify/__init__.py +1 -0
- tokentray-0.1.0/src/tokentray/notify/configuration.py +94 -0
- tokentray-0.1.0/src/tokentray/notify/dispatcher.py +100 -0
- tokentray-0.1.0/src/tokentray/notify/formatting.py +73 -0
- tokentray-0.1.0/src/tokentray/notify/webhook.py +305 -0
- tokentray-0.1.0/src/tokentray/polling.py +84 -0
- tokentray-0.1.0/src/tokentray/providers/__init__.py +27 -0
- tokentray-0.1.0/src/tokentray/providers/base.py +186 -0
- tokentray-0.1.0/src/tokentray/providers/claude.py +261 -0
- tokentray-0.1.0/src/tokentray/providers/codex.py +328 -0
- tokentray-0.1.0/src/tokentray/resources/fonts/OFL.txt +94 -0
- tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-Bold.otf +0 -0
- tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-Regular.otf +0 -0
- tokentray-0.1.0/src/tokentray/resources/fonts/Pretendard-SemiBold.otf +0 -0
- tokentray-0.1.0/src/tokentray/resources/providers/OpenAI-black-monoblossom.svg +15 -0
- tokentray-0.1.0/src/tokentray/resources/providers/OpenAI-white-monoblossom.svg +15 -0
- tokentray-0.1.0/src/tokentray/resources/providers/SOURCES.md +27 -0
- tokentray-0.1.0/src/tokentray/resources/providers/claude.svg +1 -0
- tokentray-0.1.0/src/tokentray/resources/providers/simple-icons-LICENSE.md +30 -0
- tokentray-0.1.0/src/tokentray/setup_display.py +52 -0
- tokentray-0.1.0/src/tokentray/setup_wizard.py +190 -0
- tokentray-0.1.0/src/tokentray/ui/__init__.py +1 -0
- tokentray-0.1.0/src/tokentray/ui/elided.py +29 -0
- tokentray-0.1.0/src/tokentray/ui/fonts.py +26 -0
- tokentray-0.1.0/src/tokentray/ui/icons.py +200 -0
- tokentray-0.1.0/src/tokentray/ui/integration.py +326 -0
- tokentray-0.1.0/src/tokentray/ui/loading_overlay.py +68 -0
- tokentray-0.1.0/src/tokentray/ui/panel.py +336 -0
- tokentray-0.1.0/src/tokentray/ui/popup.py +487 -0
- tokentray-0.1.0/src/tokentray/ui/provider_icons.py +59 -0
- tokentray-0.1.0/src/tokentray/ui/refresh_feedback.py +71 -0
- tokentray-0.1.0/src/tokentray/ui/theme.py +126 -0
- tokentray-0.1.0/src/tokentray/ui/tray.py +235 -0
- tokentray-0.1.0/src/tokentray/ui/wrapping.py +28 -0
- tokentray-0.1.0/tests/conftest.py +129 -0
- tokentray-0.1.0/tests/manual_connection_ui.py +126 -0
- tokentray-0.1.0/tests/test_alerts.py +235 -0
- tokentray-0.1.0/tests/test_cli_and_notify.py +391 -0
- tokentray-0.1.0/tests/test_compute.py +162 -0
- tokentray-0.1.0/tests/test_connection_ui.py +112 -0
- tokentray-0.1.0/tests/test_connections.py +174 -0
- tokentray-0.1.0/tests/test_core.py +282 -0
- tokentray-0.1.0/tests/test_integration_layout.py +197 -0
- tokentray-0.1.0/tests/test_ipc_buffered.py +27 -0
- tokentray-0.1.0/tests/test_ipc_reply_read.py +56 -0
- tokentray-0.1.0/tests/test_keychain_refusal.py +157 -0
- tokentray-0.1.0/tests/test_language_switch.py +129 -0
- tokentray-0.1.0/tests/test_linux_lifecycle.py +59 -0
- tokentray-0.1.0/tests/test_login_recovery.py +183 -0
- tokentray-0.1.0/tests/test_notification_preview.py +258 -0
- tokentray-0.1.0/tests/test_notification_routing.py +146 -0
- tokentray-0.1.0/tests/test_notifications.py +257 -0
- tokentray-0.1.0/tests/test_panel_layout.py +172 -0
- tokentray-0.1.0/tests/test_platform.py +619 -0
- tokentray-0.1.0/tests/test_provider_identity.py +125 -0
- tokentray-0.1.0/tests/test_providers.py +468 -0
- tokentray-0.1.0/tests/test_refresh_feedback.py +149 -0
- tokentray-0.1.0/tests/test_release_version.py +25 -0
- tokentray-0.1.0/tests/test_screenshots.py +44 -0
- tokentray-0.1.0/tests/test_setup_connections.py +134 -0
- tokentray-0.1.0/tests/test_state_reset.py +200 -0
- tokentray-0.1.0/tests/test_ui.py +680 -0
- 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,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.
|
tokentray-0.1.0/LICENSE
ADDED
|
@@ -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.
|