codex-reset-watch 0.20.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 (84) hide show
  1. codex_reset_watch-0.20.0/.github/workflows/ci.yml +109 -0
  2. codex_reset_watch-0.20.0/.github/workflows/release.yml +74 -0
  3. codex_reset_watch-0.20.0/.gitignore +297 -0
  4. codex_reset_watch-0.20.0/.python-version +1 -0
  5. codex_reset_watch-0.20.0/CHANGELOG.md +457 -0
  6. codex_reset_watch-0.20.0/Makefile +79 -0
  7. codex_reset_watch-0.20.0/PKG-INFO +982 -0
  8. codex_reset_watch-0.20.0/README.md +970 -0
  9. codex_reset_watch-0.20.0/config.example.json +25 -0
  10. codex_reset_watch-0.20.0/docs/images/crw-check-result-output.png +0 -0
  11. codex_reset_watch-0.20.0/docs/images/crw-config-basic-mode.png +0 -0
  12. codex_reset_watch-0.20.0/docs/images/crw-config-telegram.png +0 -0
  13. codex_reset_watch-0.20.0/docs/images/crw-reset-notification.png +0 -0
  14. codex_reset_watch-0.20.0/docs/images/current-reset-event-result.png +0 -0
  15. codex_reset_watch-0.20.0/docs/images/upcoming-event-result.png +0 -0
  16. codex_reset_watch-0.20.0/docs/research-notes.md +38 -0
  17. codex_reset_watch-0.20.0/launchd/README.md +16 -0
  18. codex_reset_watch-0.20.0/pyproject.toml +31 -0
  19. codex_reset_watch-0.20.0/scripts/install.py +292 -0
  20. codex_reset_watch-0.20.0/scripts/render_launchd.py +35 -0
  21. codex_reset_watch-0.20.0/scripts/render_systemd.py +33 -0
  22. codex_reset_watch-0.20.0/scripts/schtasks.py +16 -0
  23. codex_reset_watch-0.20.0/scripts/uninstall.py +45 -0
  24. codex_reset_watch-0.20.0/src/codex_reset_watch/__init__.py +1443 -0
  25. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/reset/reset_1.jpeg +0 -0
  26. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/reset/reset_2.jpeg +0 -0
  27. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/reset/reset_3.jpeg +0 -0
  28. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/reset/reset_4.jpeg +0 -0
  29. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/reset/reset_5.jpeg +0 -0
  30. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_1.jpeg +0 -0
  31. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_10.jpeg +0 -0
  32. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_11.png +0 -0
  33. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_2.jpeg +0 -0
  34. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_3.jpeg +0 -0
  35. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_5.jpeg +0 -0
  36. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_6.jpeg +0 -0
  37. codex_reset_watch-0.20.0/src/codex_reset_watch/assets/upcoming/upcoming_7.jpeg +0 -0
  38. codex_reset_watch-0.20.0/src/codex_reset_watch/config.py +696 -0
  39. codex_reset_watch-0.20.0/src/codex_reset_watch/console.py +62 -0
  40. codex_reset_watch-0.20.0/src/codex_reset_watch/filelock.py +61 -0
  41. codex_reset_watch-0.20.0/src/codex_reset_watch/host_identity.py +613 -0
  42. codex_reset_watch-0.20.0/src/codex_reset_watch/i18n.py +689 -0
  43. codex_reset_watch-0.20.0/src/codex_reset_watch/keys.py +272 -0
  44. codex_reset_watch-0.20.0/src/codex_reset_watch/paths.py +80 -0
  45. codex_reset_watch-0.20.0/src/codex_reset_watch/scheduler.py +558 -0
  46. codex_reset_watch-0.20.0/src/codex_reset_watch/secrets_store.py +46 -0
  47. codex_reset_watch-0.20.0/src/codex_reset_watch/ui.py +1513 -0
  48. codex_reset_watch-0.20.0/src/codex_reset_watch/update_check.py +206 -0
  49. codex_reset_watch-0.20.0/tests/__init__.py +50 -0
  50. codex_reset_watch-0.20.0/tests/fixtures/resets.json +18 -0
  51. codex_reset_watch-0.20.0/tests/fixtures/status_active_watch.json +37 -0
  52. codex_reset_watch-0.20.0/tests/fixtures/status_no_upcoming.json +9 -0
  53. codex_reset_watch-0.20.0/tests/fixtures/status_scheduled_tba.json +24 -0
  54. codex_reset_watch-0.20.0/tests/fixtures/status_unrecognized.json +4 -0
  55. codex_reset_watch-0.20.0/tests/fixtures/status_upcoming.json +17 -0
  56. codex_reset_watch-0.20.0/tests/test_cli_config.py +183 -0
  57. codex_reset_watch-0.20.0/tests/test_cli_syntax.py +281 -0
  58. codex_reset_watch-0.20.0/tests/test_config.py +297 -0
  59. codex_reset_watch-0.20.0/tests/test_config_extras.py +330 -0
  60. codex_reset_watch-0.20.0/tests/test_console.py +94 -0
  61. codex_reset_watch-0.20.0/tests/test_filelock.py +34 -0
  62. codex_reset_watch-0.20.0/tests/test_formatting.py +243 -0
  63. codex_reset_watch-0.20.0/tests/test_host_identity.py +29 -0
  64. codex_reset_watch-0.20.0/tests/test_i18n.py +128 -0
  65. codex_reset_watch-0.20.0/tests/test_install.py +72 -0
  66. codex_reset_watch-0.20.0/tests/test_install_telegram.py +135 -0
  67. codex_reset_watch-0.20.0/tests/test_integration.py +58 -0
  68. codex_reset_watch-0.20.0/tests/test_keys.py +180 -0
  69. codex_reset_watch-0.20.0/tests/test_launchd.py +81 -0
  70. codex_reset_watch-0.20.0/tests/test_parser.py +158 -0
  71. codex_reset_watch-0.20.0/tests/test_paths.py +55 -0
  72. codex_reset_watch-0.20.0/tests/test_run_check.py +330 -0
  73. codex_reset_watch-0.20.0/tests/test_scheduler.py +317 -0
  74. codex_reset_watch-0.20.0/tests/test_schtasks.py +32 -0
  75. codex_reset_watch-0.20.0/tests/test_secrets_store.py +250 -0
  76. codex_reset_watch-0.20.0/tests/test_security.py +507 -0
  77. codex_reset_watch-0.20.0/tests/test_state.py +13 -0
  78. codex_reset_watch-0.20.0/tests/test_systemd.py +87 -0
  79. codex_reset_watch-0.20.0/tests/test_ui.py +330 -0
  80. codex_reset_watch-0.20.0/tests/test_ui_menu.py +894 -0
  81. codex_reset_watch-0.20.0/tests/test_ui_mode.py +210 -0
  82. codex_reset_watch-0.20.0/tests/test_uninstall.py +11 -0
  83. codex_reset_watch-0.20.0/tests/test_update_check.py +218 -0
  84. codex_reset_watch-0.20.0/uv.lock +92 -0
@@ -0,0 +1,109 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ env:
15
+ PYTHONUTF8: "1"
16
+
17
+ jobs:
18
+ test:
19
+ name: Python 3.13 / ${{ matrix.os }}
20
+ timeout-minutes: 15
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ os: [macos-latest, ubuntu-latest, windows-latest]
25
+ runs-on: ${{ matrix.os }}
26
+ steps:
27
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
28
+ with:
29
+ persist-credentials: false
30
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
31
+ with:
32
+ python-version: "3.13"
33
+ enable-cache: true
34
+ cache-dependency-glob: uv.lock
35
+ - name: Install locked dependencies
36
+ run: uv sync --locked
37
+ - name: Lint
38
+ run: >
39
+ uv run python -m py_compile
40
+ src/codex_reset_watch/__init__.py src/codex_reset_watch/paths.py
41
+ src/codex_reset_watch/filelock.py scripts/render_launchd.py
42
+ scripts/render_systemd.py scripts/schtasks.py scripts/install.py
43
+ scripts/uninstall.py
44
+ - name: Test
45
+ run: uv run python -m unittest discover -s tests -v
46
+ - name: Build distributions
47
+ run: uv build
48
+ - name: Validate wheel and console script
49
+ shell: python
50
+ run: |
51
+ import os
52
+ import pathlib
53
+ import subprocess
54
+ import tempfile
55
+
56
+ wheel, = pathlib.Path("dist").glob("*.whl")
57
+ with tempfile.TemporaryDirectory() as directory:
58
+ environment = pathlib.Path(directory)
59
+ subprocess.run(["uv", "venv", environment], check=True)
60
+ scripts = environment / ("Scripts" if os.name == "nt" else "bin")
61
+ python = scripts / ("python.exe" if os.name == "nt" else "python")
62
+ subprocess.run(
63
+ ["uv", "pip", "install", "--python", python, wheel],
64
+ check=True,
65
+ )
66
+ suffix = ".exe" if os.name == "nt" else ""
67
+ subprocess.run([scripts / f"codex-reset-watch{suffix}", "--help"], check=True)
68
+ subprocess.run([scripts / f"crw{suffix}", "--help"], check=True)
69
+ print("console scripts OK")
70
+
71
+ # Failure-only pager. A green run sends nothing on purpose: a notification
72
+ # that arrives on every push is one nobody reads. Credentials live in repo
73
+ # secrets, never in this file — see README "CI notifications" for the two
74
+ # `gh secret set` commands that configure them.
75
+ notify-telegram:
76
+ needs: test
77
+ if: ${{ failure() && github.event_name == 'push' }}
78
+ runs-on: ubuntu-latest
79
+ timeout-minutes: 5
80
+ steps:
81
+ - name: Notify Telegram
82
+ env:
83
+ TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
84
+ CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
85
+ run: |
86
+ # Unconfigured secrets are a valid state (a fork, or a clone whose
87
+ # owner never set them up). Skip quietly instead of failing the job
88
+ # and reporting a second, misleading red X on top of the real one.
89
+ if [ -z "$TOKEN" ] || [ -z "$CHAT_ID" ]; then
90
+ echo "::notice::TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID not set — skipping notification"
91
+ exit 0
92
+ fi
93
+ # parse_mode=HTML: a branch or commit subject containing < & > would
94
+ # otherwise produce unparseable markup and Telegram would 400.
95
+ esc() { printf '%s' "$1" | sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'; }
96
+ ci_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
97
+ message="🚨 <b>CI failed</b>
98
+
99
+ <b>Repository:</b> <code>$(esc "$GITHUB_REPOSITORY")</code>
100
+ <b>Branch:</b> <code>$(esc "$GITHUB_REF_NAME")</code>
101
+ <b>Commit:</b> <code>$(esc "${GITHUB_SHA::7}")</code>
102
+
103
+ <a href=\"$ci_url\">Open failed CI run</a>"
104
+ curl --fail --silent --show-error --max-time 30 \
105
+ --data-urlencode "chat_id=$CHAT_ID" \
106
+ --data-urlencode "parse_mode=HTML" \
107
+ --data-urlencode "disable_web_page_preview=true" \
108
+ --data-urlencode "text=$message" \
109
+ "https://api.telegram.org/bot$TOKEN/sendMessage"
@@ -0,0 +1,74 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: release-${{ github.ref }}
13
+
14
+ env:
15
+ PYTHONUTF8: "1"
16
+
17
+ jobs:
18
+ release:
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 15
21
+ # Trusted Publishing (OIDC): the PyPI publisher is bound to this
22
+ # environment, whose required reviewer gates every upload.
23
+ environment: pypi
24
+ permissions:
25
+ contents: write
26
+ id-token: write
27
+ steps:
28
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
29
+ with:
30
+ persist-credentials: false
31
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
32
+ with:
33
+ python-version: "3.13"
34
+ enable-cache: true
35
+ cache-dependency-glob: uv.lock
36
+ - name: Install locked dependencies
37
+ run: uv sync --locked
38
+ - name: Check, test, and build
39
+ run: |
40
+ uv run python -m compileall -q src scripts
41
+ uv run python -m unittest discover -s tests -v
42
+ uv build
43
+ - name: Validate release wheel
44
+ run: |
45
+ uv venv .release-venv
46
+ uv pip install --python .release-venv/bin/python dist/*.whl
47
+ .release-venv/bin/codex-reset-watch --help
48
+ .release-venv/bin/crw --help
49
+ # The wheel must report the version being tagged, so a tag that
50
+ # disagrees with pyproject.toml fails before anything reaches PyPI.
51
+ reported="$(.release-venv/bin/crw --version)"
52
+ expected="codex-reset-watch ${GITHUB_REF_NAME#v}"
53
+ if [ "$reported" != "$expected" ]; then
54
+ echo "::error::wheel reports '$reported', expected '$expected'"
55
+ exit 1
56
+ fi
57
+ - name: Publish to PyPI
58
+ run: uv publish --trusted-publishing always
59
+
60
+ # After publish: uv publish uploads dist/*, and SHA256SUMS is not a distribution.
61
+ # The GitHub release comes last on purpose: the in-app update check reads
62
+ # releases/latest, so a release must never exist before its PyPI upload.
63
+ - name: Create checksums
64
+ run: sha256sum dist/* > dist/SHA256SUMS
65
+ - name: Create GitHub release
66
+ env:
67
+ GH_TOKEN: ${{ github.token }}
68
+ run: |
69
+ gh api "repos/${GITHUB_REPOSITORY}/git/refs/tags/$GITHUB_REF_NAME" >/dev/null
70
+ if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
71
+ gh release upload "$GITHUB_REF_NAME" dist/* --clobber
72
+ else
73
+ gh release create "$GITHUB_REF_NAME" dist/* --generate-notes --title "$GITHUB_REF_NAME" --verify-tag
74
+ fi
@@ -0,0 +1,297 @@
1
+ # ============================================================
2
+ # codex-reset-watch
3
+ # Python + uv
4
+ # macOS / Windows / Linux
5
+ # ============================================================
6
+
7
+
8
+ # ------------------------------------------------------------
9
+ # Python
10
+ # ------------------------------------------------------------
11
+
12
+ __pycache__/
13
+ *.py[cod]
14
+ *$py.class
15
+ *.so
16
+
17
+ # Python virtual environments
18
+ .venv/
19
+ venv/
20
+ env/
21
+ ENV/
22
+ env.bak/
23
+ venv.bak/
24
+
25
+ # Python packaging / build
26
+ build/
27
+ dist/
28
+ downloads/
29
+ eggs/
30
+ .eggs/
31
+ lib/
32
+ lib64/
33
+ parts/
34
+ sdist/
35
+ var/
36
+ wheels/
37
+ share/python-wheels/
38
+
39
+ *.egg-info/
40
+ .installed.cfg
41
+ *.egg
42
+ MANIFEST
43
+
44
+ # PyInstaller
45
+ *.manifest
46
+ *.spec
47
+
48
+ # pip
49
+ pip-log.txt
50
+ pip-delete-this-directory.txt
51
+
52
+
53
+ # ------------------------------------------------------------
54
+ # uv
55
+ # ------------------------------------------------------------
56
+
57
+ # uv local cache
58
+ .uv/
59
+
60
+ # Keep these tracked:
61
+ # uv.lock
62
+ # .python-version
63
+
64
+
65
+ # ------------------------------------------------------------
66
+ # Tests / Coverage
67
+ # ------------------------------------------------------------
68
+
69
+ .pytest_cache/
70
+ .coverage
71
+ .coverage.*
72
+ htmlcov/
73
+ coverage.xml
74
+ coverage.json
75
+
76
+ .tox/
77
+ .nox/
78
+
79
+ *.cover
80
+ *.py,cover
81
+ .hypothesis/
82
+
83
+
84
+ # ------------------------------------------------------------
85
+ # Type checkers / linters
86
+ # ------------------------------------------------------------
87
+
88
+ .mypy_cache/
89
+ .dmypy.json
90
+ dmypy.json
91
+
92
+ .pyre/
93
+ .pytype/
94
+
95
+ .ruff_cache/
96
+
97
+ .pyright/
98
+
99
+
100
+ # ------------------------------------------------------------
101
+ # Runtime / Logs / Temporary files
102
+ # ------------------------------------------------------------
103
+
104
+ *.log
105
+ *.log.*
106
+ logs/
107
+ log/
108
+
109
+ *.tmp
110
+ *.temp
111
+ *.swp
112
+ *.swo
113
+ *.bak
114
+
115
+ tmp/
116
+ temp/
117
+
118
+ *.pid
119
+ *.pid.lock
120
+
121
+
122
+ # ------------------------------------------------------------
123
+ # Secrets / Environment variables
124
+ # ------------------------------------------------------------
125
+
126
+ .env
127
+ .env.*
128
+ !.env.example
129
+
130
+ *.pem
131
+ *.key
132
+ *.crt
133
+ *.p12
134
+ *.pfx
135
+
136
+ secrets/
137
+ credentials/
138
+
139
+
140
+ # ------------------------------------------------------------
141
+ # Local application state
142
+ # ------------------------------------------------------------
143
+
144
+ state.json
145
+ config.local.json
146
+ *.state.json
147
+
148
+ runtime/
149
+ data/
150
+
151
+
152
+ # ------------------------------------------------------------
153
+ # macOS
154
+ # ------------------------------------------------------------
155
+
156
+ .DS_Store
157
+ .AppleDouble
158
+ .LSOverride
159
+
160
+ # Icon must end with two \r
161
+ Icon
162
+
163
+ ._*
164
+
165
+ # Files that might appear in the root of a volume
166
+ .DocumentRevisions-V100
167
+ .fseventsd
168
+ .Spotlight-V100
169
+ .TemporaryItems
170
+ .Trashes
171
+ .VolumeIcon.icns
172
+ .com.apple.timemachine.donotpresent
173
+
174
+ # AFP share files
175
+ .AppleDB
176
+ .AppleDesktop
177
+ Network Trash Folder
178
+ Temporary Items
179
+ .apdisk
180
+
181
+
182
+ # ------------------------------------------------------------
183
+ # Windows
184
+ # ------------------------------------------------------------
185
+
186
+ Thumbs.db
187
+ Thumbs.db:encryptable
188
+ ehthumbs.db
189
+ ehthumbs_vista.db
190
+
191
+ Desktop.ini
192
+
193
+ $RECYCLE.BIN/
194
+
195
+ *.cab
196
+ *.msi
197
+ *.msix
198
+ *.msm
199
+ *.msp
200
+
201
+ # Windows shortcuts
202
+ *.lnk
203
+
204
+ # Windows dump files
205
+ *.dmp
206
+
207
+
208
+ # ------------------------------------------------------------
209
+ # Linux
210
+ # ------------------------------------------------------------
211
+
212
+ *~
213
+ .fuse_hidden*
214
+ .directory
215
+ .Trash-*
216
+ .nfs*
217
+
218
+
219
+ # ------------------------------------------------------------
220
+ # Visual Studio Code
221
+ # ------------------------------------------------------------
222
+
223
+ .vscode/
224
+
225
+ # If you later want to share selected VS Code settings:
226
+ # .vscode/*
227
+ # !.vscode/settings.json
228
+ # !.vscode/extensions.json
229
+ # !.vscode/tasks.json
230
+ # !.vscode/launch.json
231
+
232
+
233
+ # ------------------------------------------------------------
234
+ # JetBrains IDEs
235
+ # PyCharm / IntelliJ / WebStorm
236
+ # ------------------------------------------------------------
237
+
238
+ .idea/
239
+ *.iml
240
+ *.iws
241
+ *.ipr
242
+
243
+
244
+ # ------------------------------------------------------------
245
+ # Vim / Neovim
246
+ # ------------------------------------------------------------
247
+
248
+ *.swp
249
+ *.swo
250
+ *.swn
251
+ Session.vim
252
+ .netrwhist
253
+
254
+
255
+ # ------------------------------------------------------------
256
+ # Emacs
257
+ # ------------------------------------------------------------
258
+
259
+ *~
260
+ \#*\#
261
+ .\#*
262
+ .dir-locals.el
263
+
264
+
265
+ # ------------------------------------------------------------
266
+ # Archives generated locally
267
+ # ------------------------------------------------------------
268
+
269
+ *.zip
270
+ *.tar
271
+ *.tar.gz
272
+ *.tgz
273
+ *.7z
274
+ *.rar
275
+
276
+
277
+ # ------------------------------------------------------------
278
+ # Project-specific local artifacts
279
+ # ------------------------------------------------------------
280
+
281
+ # launchd-generated local plist copies, if generated inside repo
282
+ generated/
283
+ generated-plists/
284
+
285
+ # Local API response/debug dumps
286
+ api-dumps/
287
+ debug/
288
+ *.debug.json
289
+ *.stackdump
290
+
291
+ # Local test output
292
+ test-results/
293
+ .playwright-mcp/
294
+
295
+ # CodeGraph index directory
296
+ .codegraph/
297
+
@@ -0,0 +1 @@
1
+ 3.13