cdx-manager 0.7.6 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/changelogs/CHANGELOGS_0_7_8.md +30 -0
- package/checksums/release-archives.json +4 -0
- package/install.ps1 +15 -0
- package/install.sh +27 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +56 -25
- package/src/cli_commands.py +47 -27
- package/src/cli_render.py +4 -0
- package/src/health.py +12 -0
- package/src/provider_runtime.py +35 -6
- package/src/session_service.py +13 -1
- package/src/update_check.py +103 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CDX Manager
|
|
2
2
|
|
|
3
|
-
[](LICENSE) ](LICENSE)  
|
|
4
4
|
|
|
5
5
|
**Run multiple Codex, Claude, Antigravity, and Ollama sessions from one terminal. Switch between accounts instantly.**
|
|
6
6
|
|
|
@@ -41,7 +41,7 @@ One command to launch any session. Zero auth juggling.
|
|
|
41
41
|
- **Session control.** Disable a session without deleting it when an account is temporarily out of credits; disabled sessions remain visible and sort last.
|
|
42
42
|
- **Persistent launch settings.** Pin per-session power, permission, and fast-mode preferences once; `cdx` reapplies them on every launch until you unset them.
|
|
43
43
|
- **Launch history.** Inspect recent launches with provider, result, duration, working directory, launch settings, and transcript path.
|
|
44
|
-
- **Update prompts.** Periodic update checks surface `cdx update` directly in the `cdx`, `cdx status`, and launch output when a newer release is available.
|
|
44
|
+
- **Update prompts.** Periodic update checks surface `cdx update` directly in the `cdx`, `cdx status`, and launch output when a newer release is available. When `logics-manager` is installed, `cdx` can also suggest `logics-manager self-update`.
|
|
45
45
|
- **Shared handoff context.** Keep a per-workspace Markdown context, or build one from a source session transcript, and install it into another assistant session before switching providers or accounts.
|
|
46
46
|
- **Passive status resolution.** Codex status is read from the local Codex app-server rate-limit API when available, with legacy transcript/history parsing kept as a fallback.
|
|
47
47
|
- **Session transcript capture.** Every launch is recorded to a local log file via `script`, giving you a full terminal transcript for each session.
|
|
@@ -136,7 +136,7 @@ For a specific version:
|
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
138
|
curl -fsSL https://raw.githubusercontent.com/AlexAgo83/cdx-manager/main/install.sh -o install.sh
|
|
139
|
-
CDX_VERSION=v0.7.
|
|
139
|
+
CDX_VERSION=v0.7.8 sh install.sh
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
From source:
|
|
@@ -271,6 +271,7 @@ cdx set --sessions all --permission auto
|
|
|
271
271
|
cdx set --provider ollama --model llama3.2
|
|
272
272
|
cdx set work --priority 80
|
|
273
273
|
cdx set work --rtk on
|
|
274
|
+
cdx set work --logics off
|
|
274
275
|
cdx power all low
|
|
275
276
|
cdx perm provider:claude review
|
|
276
277
|
cdx model provider:ollama llama3.2
|
|
@@ -286,12 +287,13 @@ cdx unset --sessions work,personal --fast
|
|
|
286
287
|
cdx unset --provider claude --permission
|
|
287
288
|
cdx unset work --priority
|
|
288
289
|
cdx unset work --rtk
|
|
290
|
+
cdx unset work --logics
|
|
289
291
|
cdx unset work --all
|
|
290
292
|
cdx power all default
|
|
291
293
|
cdx model provider:ollama default
|
|
292
294
|
```
|
|
293
295
|
|
|
294
|
-
`--model` maps to Codex `--model`, Claude `--model`, and Ollama `ollama run <model>`. `--power` maps to Codex `model_reasoning_effort` and Claude `--effort`. `--permission` maps to provider-native permission flags. `--fast on` clears the stored power setting and uses low effort; setting `--power` again disables fast mode. `--priority` is a 0..100 selector preference used as a tie-breaker after readiness and availability. `--rtk on` injects a launch instruction that encourages assistants to use RTK (`rtk <command>`) for noisy terminal commands when RTK is available, while keeping raw commands for exact output.
|
|
296
|
+
`--model` maps to Codex `--model`, Claude `--model`, and Ollama `ollama run <model>`. `--power` maps to Codex `model_reasoning_effort` and Claude `--effort`. `--permission` maps to provider-native permission flags. `--fast on` clears the stored power setting and uses low effort; setting `--power` again disables fast mode. `--priority` is a 0..100 selector preference used as a tie-breaker after readiness and availability. `--rtk on` injects a launch instruction that encourages assistants to use RTK (`rtk <command>`) for noisy terminal commands when RTK is available, while keeping raw commands for exact output. Logics guidance is auto-enabled when `logics-manager` is available; use `--logics off` to disable that guidance for a session, or `--logics on` to pin it explicitly.
|
|
295
297
|
|
|
296
298
|
### Launch History
|
|
297
299
|
|
|
@@ -329,8 +331,8 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
329
331
|
| `cdx config <name> [--json]` | Show persistent launch settings for a session |
|
|
330
332
|
| `cdx configs [--json]` | Show persistent launch settings for all sessions in one table |
|
|
331
333
|
| `cdx power\|perm\|fast\|model <name\|all\|provider:PROVIDER\|a,b> <value\|default> [--json]` | Shortcut commands for setting or clearing one launch setting |
|
|
332
|
-
| `cdx set <name>\|--sessions all\|a,b\|--provider PROVIDER [--power low\|medium\|high\|xhigh\|max] [--permission review\|default\|auto\|full] [--fast on\|off] [--rtk on\|off] [--model MODEL] [--priority 0..100] [--json]` | Persist launch settings for one or more sessions |
|
|
333
|
-
| `cdx unset <name>\|--sessions all\|a,b\|--provider PROVIDER (--power\|--permission\|--fast\|--rtk\|--model\|--priority\|--all) [--json]` | Remove persisted launch settings and fall back to provider defaults |
|
|
334
|
+
| `cdx set <name>\|--sessions all\|a,b\|--provider PROVIDER [--power low\|medium\|high\|xhigh\|max] [--permission review\|default\|auto\|full] [--fast on\|off] [--rtk on\|off] [--logics on\|off] [--model MODEL] [--priority 0..100] [--json]` | Persist launch settings for one or more sessions |
|
|
335
|
+
| `cdx unset <name>\|--sessions all\|a,b\|--provider PROVIDER (--power\|--permission\|--fast\|--rtk\|--logics\|--model\|--priority\|--all) [--json]` | Remove persisted launch settings and fall back to provider defaults |
|
|
334
336
|
| `cdx history [name] [--limit N] [--summary] [--since 7d\|today\|DATE] [--from DATE] [--to DATE] [--json]` | Show recent launch history or aggregate total launch time per assistant, optionally filtered by period |
|
|
335
337
|
| `cdx last [--json]` | Launch the most recent existing session from launch history |
|
|
336
338
|
| `cdx context show\|path\|init\|edit\|clear\|set [text...] [--json]` | Manage the shared Markdown context for the current workspace |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog (`0.7.6 -> 0.7.8`)
|
|
2
|
+
|
|
3
|
+
Release date: 2026-06-08
|
|
4
|
+
|
|
5
|
+
## Launch Guidance
|
|
6
|
+
|
|
7
|
+
- Added update guidance for `logics-manager` so `cdx` can surface a newer companion CLI version alongside the existing `cdx-manager` update notice.
|
|
8
|
+
- Added RTK launch preference handling so noisy assistant shell commands can be wrapped with `rtk` when the session setting asks for filtered command output.
|
|
9
|
+
- Extended provider launch metadata and health checks so companion tool hints appear without making `logics-manager` or RTK hard runtime dependencies.
|
|
10
|
+
|
|
11
|
+
## Coverage and Tests
|
|
12
|
+
|
|
13
|
+
- Added regression coverage for multi-tool update warnings, launch notice rendering, provider runtime launch metadata, and RTK preference handling.
|
|
14
|
+
|
|
15
|
+
## Release Metadata
|
|
16
|
+
|
|
17
|
+
- Updated package metadata, CLI version output, README badge, pinned installer example, and release changelog to `v0.7.8`.
|
|
18
|
+
|
|
19
|
+
## Validation and Regression Evidence
|
|
20
|
+
|
|
21
|
+
- `npm run lint`
|
|
22
|
+
- `npm test`
|
|
23
|
+
- `python -m unittest discover -s test -p 'test_*_py.py'`
|
|
24
|
+
- `python3 -m logics_manager lint --require-status`
|
|
25
|
+
- `git diff --check`
|
|
26
|
+
- `node bin/cdx.js --version`
|
|
27
|
+
- `python3 bin/cdx --version`
|
|
28
|
+
- `npm --cache /private/tmp/cdx-npm-cache pack --dry-run`
|
|
29
|
+
- `python -m build`
|
|
30
|
+
- `python -m twine check dist/*`
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"v0.7.5": {
|
|
53
53
|
"github_tarball_sha256": "18f5f6230bb5704913a90cfe2bfc5f55599c6ca0813bfd9f4bc97fc9f19e074a",
|
|
54
54
|
"github_zip_sha256": "cafd400c65c255aa9569853ec9d1cc80cf691ff849660496779334ee7abf71b4"
|
|
55
|
+
},
|
|
56
|
+
"v0.7.6": {
|
|
57
|
+
"github_tarball_sha256": "cf6390c2071c710edd7c55d89fd75914e1d94ae5a62330d6fe15029484f88c13",
|
|
58
|
+
"github_zip_sha256": "f58be3078daaa9523053dab7b59d681dfb359d5f2a94276aa46000dd7f1360ed"
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
}
|
package/install.ps1
CHANGED
|
@@ -11,6 +11,12 @@ $repo = "AlexAgo83/cdx-manager"
|
|
|
11
11
|
if (-not $ChecksumsUrl) {
|
|
12
12
|
$ChecksumsUrl = "https://raw.githubusercontent.com/$repo/main/checksums/release-archives.json"
|
|
13
13
|
}
|
|
14
|
+
$defaultChecksumsUrl = "https://raw.githubusercontent.com/$repo/main/checksums/release-archives.json"
|
|
15
|
+
$checksumsApiUrl = if ($env:CDX_CHECKSUMS_API_URL) {
|
|
16
|
+
$env:CDX_CHECKSUMS_API_URL
|
|
17
|
+
} else {
|
|
18
|
+
"https://api.github.com/repos/$repo/contents/checksums/release-archives.json?ref=main"
|
|
19
|
+
}
|
|
14
20
|
|
|
15
21
|
function Has-Command {
|
|
16
22
|
param([string]$Name)
|
|
@@ -56,6 +62,15 @@ try {
|
|
|
56
62
|
} catch {
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
if ((-not $Sha256) -and ($ChecksumsUrl -eq $defaultChecksumsUrl)) {
|
|
66
|
+
try {
|
|
67
|
+
$checksumsResponse = Invoke-RestMethod -Uri $checksumsApiUrl
|
|
68
|
+
$checksumsJson = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($checksumsResponse.content))
|
|
69
|
+
$checksums = $checksumsJson | ConvertFrom-Json
|
|
70
|
+
$Sha256 = $checksums.releases.$tag.github_zip_sha256
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
59
74
|
if ($Sha256) {
|
|
60
75
|
$actualSha256 = (Get-FileHash -Algorithm SHA256 -Path $archivePath).Hash.ToLowerInvariant()
|
|
61
76
|
if ($actualSha256 -ne $Sha256.ToLowerInvariant()) {
|
package/install.sh
CHANGED
|
@@ -6,7 +6,9 @@ VERSION="${CDX_VERSION:-}"
|
|
|
6
6
|
PREFIX="${PREFIX:-$HOME/.local}"
|
|
7
7
|
BIN_DIR="${BIN_DIR:-$PREFIX/bin}"
|
|
8
8
|
INSTALL_ROOT="${CDX_INSTALL_ROOT:-$PREFIX/share/cdx-manager}"
|
|
9
|
-
|
|
9
|
+
DEFAULT_CHECKSUMS_URL="https://raw.githubusercontent.com/$REPO/main/checksums/release-archives.json"
|
|
10
|
+
CHECKSUMS_URL="${CDX_CHECKSUMS_URL:-$DEFAULT_CHECKSUMS_URL}"
|
|
11
|
+
CHECKSUMS_API_URL="${CDX_CHECKSUMS_API_URL:-https://api.github.com/repos/$REPO/contents/checksums/release-archives.json?ref=main}"
|
|
10
12
|
|
|
11
13
|
need() {
|
|
12
14
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
@@ -51,6 +53,27 @@ if value:
|
|
|
51
53
|
' "$1"
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
resolve_expected_sha256_from_api() {
|
|
57
|
+
curl -fsSL "$CHECKSUMS_API_URL" |
|
|
58
|
+
python3 -c '
|
|
59
|
+
import base64
|
|
60
|
+
import json
|
|
61
|
+
import sys
|
|
62
|
+
|
|
63
|
+
tag = sys.argv[1]
|
|
64
|
+
try:
|
|
65
|
+
response = json.load(sys.stdin)
|
|
66
|
+
payload = json.loads(base64.b64decode(response.get("content") or b"").decode("utf-8"))
|
|
67
|
+
except Exception:
|
|
68
|
+
raise SystemExit(1)
|
|
69
|
+
|
|
70
|
+
release = (payload.get("releases") or {}).get(tag) or {}
|
|
71
|
+
value = release.get("github_tarball_sha256")
|
|
72
|
+
if value:
|
|
73
|
+
print(value)
|
|
74
|
+
' "$1"
|
|
75
|
+
}
|
|
76
|
+
|
|
54
77
|
if [ -z "$VERSION" ]; then
|
|
55
78
|
VERSION="$(
|
|
56
79
|
curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" |
|
|
@@ -76,6 +99,9 @@ EXPECTED_SHA256="${CDX_SHA256:-}"
|
|
|
76
99
|
if [ -z "$EXPECTED_SHA256" ]; then
|
|
77
100
|
EXPECTED_SHA256="$(resolve_expected_sha256 "$TAG" 2>/dev/null || true)"
|
|
78
101
|
fi
|
|
102
|
+
if [ -z "$EXPECTED_SHA256" ] && [ "$CHECKSUMS_URL" = "$DEFAULT_CHECKSUMS_URL" ]; then
|
|
103
|
+
EXPECTED_SHA256="$(resolve_expected_sha256_from_api "$TAG" 2>/dev/null || true)"
|
|
104
|
+
fi
|
|
79
105
|
|
|
80
106
|
if [ -n "$EXPECTED_SHA256" ]; then
|
|
81
107
|
ACTUAL_SHA256="$(sha256_file "$TMP_DIR/cdx-manager.tar.gz")"
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
|
@@ -58,9 +58,9 @@ from .status_view import (
|
|
|
58
58
|
_format_status_detail,
|
|
59
59
|
_format_status_rows,
|
|
60
60
|
)
|
|
61
|
-
from .update_check import check_for_update
|
|
61
|
+
from .update_check import check_for_update, check_logics_manager_for_update
|
|
62
62
|
|
|
63
|
-
VERSION = "0.7.
|
|
63
|
+
VERSION = "0.7.8"
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
# ---------------------------------------------------------------------------
|
|
@@ -84,8 +84,8 @@ def _print_help(use_color=False):
|
|
|
84
84
|
f" {_style('cdx config <name> [--json]', '36', use_color)}",
|
|
85
85
|
f" {_style('cdx configs [--json]', '36', use_color)}",
|
|
86
86
|
f" {_style('cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]', '36', use_color)}",
|
|
87
|
-
f" {_style('cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power low|medium|high|xhigh|max] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--model MODEL] [--priority 0..100] [--json]', '36', use_color)}",
|
|
88
|
-
f" {_style('cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--model|--priority|--all) [--json]', '36', use_color)}",
|
|
87
|
+
f" {_style('cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power low|medium|high|xhigh|max] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--model MODEL] [--priority 0..100] [--json]', '36', use_color)}",
|
|
88
|
+
f" {_style('cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--logics|--model|--priority|--all) [--json]', '36', use_color)}",
|
|
89
89
|
f" {_style('cdx history [name] [--limit N] [--summary] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
90
90
|
f" {_style('cdx stats [name] [--since 7d|today|DATE] [--from DATE] [--to DATE] [--json]', '36', use_color)}",
|
|
91
91
|
f" {_style('cdx last [--json]', '36', use_color)}",
|
|
@@ -158,22 +158,52 @@ def _get_update_notice(service, env, options):
|
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
def
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
def _get_update_notices(service, env, options):
|
|
162
|
+
notices = []
|
|
163
|
+
cdx_notice = _get_update_notice(service, env, options)
|
|
164
|
+
if cdx_notice:
|
|
165
|
+
notices.append({"tool": "cdx-manager", **cdx_notice})
|
|
166
|
+
checker = options.get("checkLogicsManagerForUpdate") or check_logics_manager_for_update
|
|
167
|
+
logics_notice = checker(
|
|
168
|
+
service["base_dir"],
|
|
169
|
+
env=env,
|
|
170
|
+
now_fn=options.get("now"),
|
|
171
|
+
)
|
|
172
|
+
if logics_notice:
|
|
173
|
+
notices.append(logics_notice)
|
|
174
|
+
return notices
|
|
171
175
|
|
|
172
176
|
|
|
173
|
-
def
|
|
174
|
-
if
|
|
177
|
+
def _update_warning_payload(notices):
|
|
178
|
+
if isinstance(notices, dict):
|
|
179
|
+
notices = [notices]
|
|
180
|
+
if not notices:
|
|
181
|
+
return []
|
|
182
|
+
warnings = []
|
|
183
|
+
for notice in notices:
|
|
184
|
+
tool = notice.get("tool") or "cdx-manager"
|
|
185
|
+
current = notice.get("current_version") or VERSION
|
|
186
|
+
command = notice.get("update_command") or ("cdx update" if tool == "cdx-manager" else None)
|
|
187
|
+
message = f"Update available: {tool} {notice['latest_version']} (current {current})"
|
|
188
|
+
if command:
|
|
189
|
+
message = f"{message}. Run: {command}"
|
|
190
|
+
warnings.append({
|
|
191
|
+
"code": "update_available" if tool == "cdx-manager" else f"{tool.replace('-', '_')}_update_available",
|
|
192
|
+
"message": message,
|
|
193
|
+
"tool": tool,
|
|
194
|
+
"latest_version": notice["latest_version"],
|
|
195
|
+
"current_version": current,
|
|
196
|
+
"update_command": command,
|
|
197
|
+
"url": notice.get("url"),
|
|
198
|
+
})
|
|
199
|
+
return warnings
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _update_warning_text(notices):
|
|
203
|
+
payloads = _update_warning_payload(notices)
|
|
204
|
+
if not payloads:
|
|
175
205
|
return None
|
|
176
|
-
return
|
|
206
|
+
return "\n".join(payload["message"] for payload in payloads)
|
|
177
207
|
|
|
178
208
|
|
|
179
209
|
# ---------------------------------------------------------------------------
|
|
@@ -216,15 +246,16 @@ def main(argv, options=None):
|
|
|
216
246
|
|
|
217
247
|
if argv == ["--json"]:
|
|
218
248
|
rows = service["format_list_rows"]()
|
|
219
|
-
|
|
220
|
-
out(f"{json.dumps(_list_json_payload(rows,
|
|
249
|
+
notices = _get_update_notices(service, env, options)
|
|
250
|
+
out(f"{json.dumps(_list_json_payload(rows, notices=notices), indent=2)}\n")
|
|
221
251
|
return 0
|
|
222
252
|
|
|
223
253
|
if not argv:
|
|
224
|
-
|
|
254
|
+
notices = _get_update_notices(service, env, options)
|
|
225
255
|
out(f"{_format_sessions(service, use_color=use_color)}\n")
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
warning_text = _update_warning_text(notices)
|
|
257
|
+
if warning_text:
|
|
258
|
+
out(f"{_style(warning_text, '33', use_color)}\n")
|
|
228
259
|
return 0
|
|
229
260
|
|
|
230
261
|
command, *rest = argv
|
|
@@ -243,7 +274,7 @@ def main(argv, options=None):
|
|
|
243
274
|
"stdin_is_tty": stdin_is_tty,
|
|
244
275
|
"version": VERSION,
|
|
245
276
|
"cwd": options.get("cwd") or os.getcwd(),
|
|
246
|
-
"
|
|
277
|
+
"update_notices": _get_update_notices(service, env, options) if command not in (
|
|
247
278
|
"add", "cp", "ren", "rename", "mv", "rmv", "clean", "doctor", "repair", "update", "ready", "notify", "next", "context", "config", "configs", "set", "unset", "power", "perm", "fast", "model", "history", "stats", "handoff", "login", "logout", "disable", "enable", "export", "import", "select", "run", "help", "version"
|
|
248
279
|
) else None,
|
|
249
280
|
"use_color": use_color,
|
|
@@ -355,13 +386,13 @@ def main(argv, options=None):
|
|
|
355
386
|
raise CdxError(f"Unknown command: {command}. Use cdx --help.")
|
|
356
387
|
|
|
357
388
|
|
|
358
|
-
def _list_json_payload(rows,
|
|
389
|
+
def _list_json_payload(rows, notices=None):
|
|
359
390
|
return {
|
|
360
391
|
"schema_version": API_SCHEMA_VERSION,
|
|
361
392
|
"ok": True,
|
|
362
393
|
"action": "list",
|
|
363
394
|
"message": "Listed known sessions",
|
|
364
|
-
"warnings": _update_warning_payload(
|
|
395
|
+
"warnings": _update_warning_payload(notices),
|
|
365
396
|
"sessions": rows,
|
|
366
397
|
}
|
|
367
398
|
|
package/src/cli_commands.py
CHANGED
|
@@ -55,8 +55,8 @@ EXPORT_USAGE = "Usage: cdx export <file> [--include-auth] [--force] [--json] [--
|
|
|
55
55
|
IMPORT_USAGE = "Usage: cdx import <file> [--force] [--json] [--sessions name1,name2] [--passphrase-env VAR]"
|
|
56
56
|
CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set [text...] [--json]"
|
|
57
57
|
HANDOFF_USAGE = "Usage: cdx handoff <name> [--json] | cdx handoff <source> <target> [--json]"
|
|
58
|
-
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power low|medium|high|xhigh|max] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--model MODEL] [--priority 0..100] [--json]"
|
|
59
|
-
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--model|--priority|--all) [--json]"
|
|
58
|
+
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power low|medium|high|xhigh|max] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--model MODEL] [--priority 0..100] [--json]"
|
|
59
|
+
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--logics|--model|--priority|--all) [--json]"
|
|
60
60
|
SETTING_ALIAS_USAGE = "Usage: cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]"
|
|
61
61
|
CONFIG_USAGE = "Usage: cdx config <name> [--json]"
|
|
62
62
|
CONFIGS_USAGE = "Usage: cdx configs [--json]"
|
|
@@ -109,23 +109,42 @@ def _write_json(ctx, payload):
|
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
def _update_notice_warning(ctx):
|
|
112
|
-
|
|
113
|
-
if not
|
|
112
|
+
notices = ctx.get("update_notices") or []
|
|
113
|
+
if not notices:
|
|
114
114
|
return None
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
warnings = _update_notice_warnings(ctx)
|
|
116
|
+
return warnings[0] if warnings else None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _update_notice_warnings(ctx):
|
|
120
|
+
warnings = []
|
|
121
|
+
for notice in ctx.get("update_notices") or []:
|
|
122
|
+
tool = notice.get("tool") or "cdx-manager"
|
|
123
|
+
current = notice.get("current_version") or ctx.get("version")
|
|
124
|
+
command = notice.get("update_command") or ("cdx update" if tool == "cdx-manager" else None)
|
|
125
|
+
message = f"Update available: {tool} {notice['latest_version']}"
|
|
126
|
+
if current:
|
|
127
|
+
message = f"{message} (current {current})"
|
|
128
|
+
if command:
|
|
129
|
+
message = f"{message}. Run: {command}"
|
|
130
|
+
warnings.append({
|
|
131
|
+
"code": "update_available" if tool == "cdx-manager" else f"{tool.replace('-', '_')}_update_available",
|
|
132
|
+
"message": message,
|
|
133
|
+
"tool": tool,
|
|
134
|
+
"latest_version": notice["latest_version"],
|
|
135
|
+
"current_version": current,
|
|
136
|
+
"update_command": command,
|
|
137
|
+
"url": notice.get("url"),
|
|
138
|
+
})
|
|
139
|
+
return warnings
|
|
121
140
|
|
|
122
141
|
|
|
123
142
|
def _write_update_notice(ctx):
|
|
124
|
-
|
|
125
|
-
if not
|
|
143
|
+
warnings = _update_notice_warnings(ctx)
|
|
144
|
+
if not warnings:
|
|
126
145
|
return
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
for warning in warnings:
|
|
147
|
+
ctx["out"](f"{_warn(warning['message'], ctx['use_color'])}\n")
|
|
129
148
|
|
|
130
149
|
|
|
131
150
|
def _format_bytes(value):
|
|
@@ -527,6 +546,7 @@ def _parse_set_args(args):
|
|
|
527
546
|
"--permission": {"key": "permission", "type": "str", "default": None},
|
|
528
547
|
"--fast": {"key": "fast", "type": "str", "default": None, "transform": _parse_fast_value},
|
|
529
548
|
"--rtk": {"key": "rtk", "type": "str", "default": None, "transform": _parse_fast_value},
|
|
549
|
+
"--logics": {"key": "logics", "type": "str", "default": None, "transform": _parse_fast_value},
|
|
530
550
|
"--model": {"key": "model", "type": "str", "default": None},
|
|
531
551
|
"--priority": {"key": "priority", "type": "str", "default": None, "transform": _parse_priority_value},
|
|
532
552
|
"--sessions": {"key": "sessions", "type": "str", "default": None, "transform": _parse_set_unset_sessions},
|
|
@@ -543,7 +563,7 @@ def _parse_set_args(args):
|
|
|
543
563
|
raise CdxError(SET_USAGE)
|
|
544
564
|
settings = {
|
|
545
565
|
key: parsed[key]
|
|
546
|
-
for key in ("power", "permission", "fast", "rtk", "model", "priority")
|
|
566
|
+
for key in ("power", "permission", "fast", "rtk", "logics", "model", "priority")
|
|
547
567
|
if parsed[key] is not None
|
|
548
568
|
}
|
|
549
569
|
if not settings:
|
|
@@ -563,6 +583,7 @@ def _parse_unset_args(args):
|
|
|
563
583
|
"--permission": {"key": "permission", "type": "bool", "default": False},
|
|
564
584
|
"--fast": {"key": "fast", "type": "bool", "default": False},
|
|
565
585
|
"--rtk": {"key": "rtk", "type": "bool", "default": False},
|
|
586
|
+
"--logics": {"key": "logics", "type": "bool", "default": False},
|
|
566
587
|
"--model": {"key": "model", "type": "bool", "default": False},
|
|
567
588
|
"--priority": {"key": "priority", "type": "bool", "default": False},
|
|
568
589
|
"--all": {"key": "all", "type": "bool", "default": False},
|
|
@@ -578,8 +599,8 @@ def _parse_unset_args(args):
|
|
|
578
599
|
raise CdxError(UNSET_USAGE)
|
|
579
600
|
if not parsed["names"] and not parsed["sessions"] and not parsed["provider"]:
|
|
580
601
|
raise CdxError(UNSET_USAGE)
|
|
581
|
-
keys = ["power", "permission", "fast", "rtk", "model", "priority"] if parsed["all"] else [
|
|
582
|
-
key for key in ("power", "permission", "fast", "rtk", "model", "priority") if parsed[key]
|
|
602
|
+
keys = ["power", "permission", "fast", "rtk", "logics", "model", "priority"] if parsed["all"] else [
|
|
603
|
+
key for key in ("power", "permission", "fast", "rtk", "logics", "model", "priority") if parsed[key]
|
|
583
604
|
]
|
|
584
605
|
if not keys:
|
|
585
606
|
raise CdxError(UNSET_USAGE)
|
|
@@ -1440,6 +1461,7 @@ def _format_launch_config(session, use_color=False):
|
|
|
1440
1461
|
("permission", "Permission"),
|
|
1441
1462
|
("fast", "Fast"),
|
|
1442
1463
|
("rtk", "RTK"),
|
|
1464
|
+
("logics", "Logics"),
|
|
1443
1465
|
("model", "Model"),
|
|
1444
1466
|
("priority", "Priority"),
|
|
1445
1467
|
]:
|
|
@@ -1460,16 +1482,18 @@ def _format_launch_config(session, use_color=False):
|
|
|
1460
1482
|
def _format_launch_settings_hint(name="<name>"):
|
|
1461
1483
|
return (
|
|
1462
1484
|
f"Set a value: cdx set {name} --power medium --permission auto "
|
|
1463
|
-
"--fast on --rtk on --model MODEL --priority 80"
|
|
1485
|
+
"--fast on --rtk on --logics on --model MODEL --priority 80"
|
|
1464
1486
|
)
|
|
1465
1487
|
|
|
1466
1488
|
|
|
1467
1489
|
def _format_launch_setting_value(launch, key, use_color=False):
|
|
1468
|
-
if key
|
|
1490
|
+
if key in ("fast", "rtk", "logics"):
|
|
1469
1491
|
if launch.get(key) is True:
|
|
1470
1492
|
return _style("on", "32", use_color)
|
|
1471
1493
|
if launch.get(key) is False:
|
|
1472
1494
|
return _style("off", "2", use_color)
|
|
1495
|
+
if key == "logics":
|
|
1496
|
+
return _dim("auto", use_color)
|
|
1473
1497
|
return _dim("default", use_color)
|
|
1474
1498
|
value = launch.get(key)
|
|
1475
1499
|
if value is None or value == "":
|
|
@@ -1494,7 +1518,7 @@ def _format_launch_configs(sessions, use_color=False):
|
|
|
1494
1518
|
_dim(_format_launch_settings_hint(), use_color),
|
|
1495
1519
|
])
|
|
1496
1520
|
rows = [[_style(value, "1", use_color) for value in [
|
|
1497
|
-
"SESSION", "PROVIDER", "POWER", "PERMISSION", "FAST", "RTK", "MODEL", "PRIORITY"
|
|
1521
|
+
"SESSION", "PROVIDER", "POWER", "PERMISSION", "FAST", "RTK", "LOGICS", "MODEL", "PRIORITY"
|
|
1498
1522
|
]]]
|
|
1499
1523
|
for session in sessions:
|
|
1500
1524
|
launch = session.get("launch") or {}
|
|
@@ -1505,6 +1529,7 @@ def _format_launch_configs(sessions, use_color=False):
|
|
|
1505
1529
|
_format_launch_setting_value(launch, "permission", use_color),
|
|
1506
1530
|
_format_launch_setting_value(launch, "fast", use_color),
|
|
1507
1531
|
_format_launch_setting_value(launch, "rtk", use_color),
|
|
1532
|
+
_format_launch_setting_value(launch, "logics", use_color),
|
|
1508
1533
|
_format_launch_setting_value(launch, "model", use_color),
|
|
1509
1534
|
_format_launch_setting_value(launch, "priority", use_color),
|
|
1510
1535
|
])
|
|
@@ -2384,9 +2409,7 @@ def handle_status(rest, ctx):
|
|
|
2384
2409
|
}
|
|
2385
2410
|
for item in refresh_errors
|
|
2386
2411
|
])
|
|
2387
|
-
|
|
2388
|
-
if update_warning:
|
|
2389
|
-
warnings.append(update_warning)
|
|
2412
|
+
warnings.extend(_update_notice_warnings(ctx))
|
|
2390
2413
|
|
|
2391
2414
|
status_progress = None if parsed["json"] else _make_status_progress(ctx)
|
|
2392
2415
|
rows = ctx["service"]["get_status_rows"](
|
|
@@ -2709,10 +2732,7 @@ def handle_update(rest, ctx):
|
|
|
2709
2732
|
|
|
2710
2733
|
def handle_launch(command, ctx, initial_prompt=None):
|
|
2711
2734
|
json_flag = "--json" in ctx.get("raw_args", ctx["options"].get("raw_args", []))
|
|
2712
|
-
|
|
2713
|
-
warnings = []
|
|
2714
|
-
if update_notice:
|
|
2715
|
-
warnings.append(_update_notice_warning(ctx))
|
|
2735
|
+
warnings = _update_notice_warnings(ctx)
|
|
2716
2736
|
session = ctx["service"]["launch_session"](command)
|
|
2717
2737
|
_ensure_session_authentication(
|
|
2718
2738
|
session,
|
package/src/cli_render.py
CHANGED
|
@@ -110,6 +110,10 @@ def _format_launch_text(launch):
|
|
|
110
110
|
parts.append(launch["permission"])
|
|
111
111
|
if launch.get("fast") is True:
|
|
112
112
|
parts.append("fast-on")
|
|
113
|
+
if launch.get("logics") is True:
|
|
114
|
+
parts.append("logics-on")
|
|
115
|
+
elif launch.get("logics") is False:
|
|
116
|
+
parts.append("logics-off")
|
|
113
117
|
return "/".join(parts) or "default"
|
|
114
118
|
|
|
115
119
|
|
package/src/health.py
CHANGED
|
@@ -47,6 +47,18 @@ def collect_health_report(service, base_dir, env=None):
|
|
|
47
47
|
rtk_path,
|
|
48
48
|
))
|
|
49
49
|
|
|
50
|
+
logics_path = shutil.which("logics-manager", path=env.get("PATH"))
|
|
51
|
+
issues.append(_issue(
|
|
52
|
+
"OK" if logics_path else "WARN",
|
|
53
|
+
"logics_manager_cli",
|
|
54
|
+
(
|
|
55
|
+
"logics-manager CLI found"
|
|
56
|
+
if logics_path
|
|
57
|
+
else "logics-manager CLI not found; Logics workflow guidance will not be auto-enabled"
|
|
58
|
+
),
|
|
59
|
+
logics_path,
|
|
60
|
+
))
|
|
61
|
+
|
|
50
62
|
script_bin = env.get("CDX_SCRIPT_BIN", "script")
|
|
51
63
|
script_path = shutil.which(script_bin, path=env.get("PATH"))
|
|
52
64
|
issues.append(_issue(
|
package/src/provider_runtime.py
CHANGED
|
@@ -19,6 +19,14 @@ RTK_PROMPT = (
|
|
|
19
19
|
"When running noisy shell commands, prefer RTK wrappers (`rtk <command>`) if `rtk` is available. "
|
|
20
20
|
"Use raw commands when exact, unfiltered output is required."
|
|
21
21
|
)
|
|
22
|
+
LOGICS_PROMPT = (
|
|
23
|
+
"When `logics-manager` is available, prefer it for Logics workflow operations: use "
|
|
24
|
+
"`logics-manager status`, `health`, `audit`, and `lint` for workflow state and validation; "
|
|
25
|
+
"`logics-manager view` for the browser viewer and focus workflows; "
|
|
26
|
+
"`logics-manager sync read-doc|list-docs|search-docs|context-pack` for bounded document context; "
|
|
27
|
+
"`logics-manager flow ...` for request/backlog/task lifecycle changes; and `logics-manager mcp ...` "
|
|
28
|
+
"when an MCP surface is the right fit."
|
|
29
|
+
)
|
|
22
30
|
LAUNCH_PERMISSION_ARGS = {
|
|
23
31
|
PROVIDER_CLAUDE: {
|
|
24
32
|
"review": ["--permission-mode", "plan"],
|
|
@@ -327,20 +335,40 @@ def _rtk_enabled(session):
|
|
|
327
335
|
return (session.get("launch") or {}).get("rtk") is True
|
|
328
336
|
|
|
329
337
|
|
|
330
|
-
def
|
|
331
|
-
|
|
338
|
+
def _logics_manager_available(path=None):
|
|
339
|
+
return shutil.which("logics-manager", path=path) is not None
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _logics_enabled(session, env=None):
|
|
343
|
+
launch = session.get("launch") or {}
|
|
344
|
+
if launch.get("logics") is False:
|
|
345
|
+
return False
|
|
346
|
+
if launch.get("logics") is True:
|
|
347
|
+
return True
|
|
348
|
+
env = env or os.environ
|
|
349
|
+
return _logics_manager_available(path=env.get("PATH"))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _with_launch_preferences(session, initial_prompt=None, env=None):
|
|
353
|
+
prompts = []
|
|
354
|
+
if _rtk_enabled(session):
|
|
355
|
+
prompts.append(RTK_PROMPT)
|
|
356
|
+
if _logics_enabled(session, env=env):
|
|
357
|
+
prompts.append(LOGICS_PROMPT)
|
|
358
|
+
if not prompts:
|
|
332
359
|
return initial_prompt
|
|
333
360
|
if initial_prompt:
|
|
334
|
-
|
|
335
|
-
return
|
|
361
|
+
prompts.append(initial_prompt)
|
|
362
|
+
return "\n\n".join(prompts)
|
|
336
363
|
|
|
337
364
|
|
|
338
365
|
def _build_launch_spec(session, cwd=None, env_override=None, initial_prompt=None, capture_transcript=True):
|
|
339
|
-
initial_prompt = _with_launch_preferences(session, initial_prompt)
|
|
340
366
|
_validate_initial_prompt(initial_prompt)
|
|
341
367
|
cwd = cwd or os.getcwd()
|
|
342
368
|
env_override = env_override or {}
|
|
343
369
|
env = {**os.environ, **env_override}
|
|
370
|
+
initial_prompt = _with_launch_preferences(session, initial_prompt, env=env)
|
|
371
|
+
_validate_initial_prompt(initial_prompt)
|
|
344
372
|
if session["provider"] == PROVIDER_CLAUDE:
|
|
345
373
|
launch = session.get("launch") or {}
|
|
346
374
|
args = ["--name", session["name"]]
|
|
@@ -418,10 +446,11 @@ def _validate_initial_prompt(initial_prompt):
|
|
|
418
446
|
|
|
419
447
|
|
|
420
448
|
def _build_headless_launch_spec(session, cwd=None, env_override=None, initial_prompt=None):
|
|
421
|
-
initial_prompt = _with_launch_preferences(session, initial_prompt)
|
|
422
449
|
_validate_initial_prompt(initial_prompt)
|
|
423
450
|
cwd = cwd or os.getcwd()
|
|
424
451
|
env = {**os.environ, **(env_override or {})}
|
|
452
|
+
initial_prompt = _with_launch_preferences(session, initial_prompt, env=env)
|
|
453
|
+
_validate_initial_prompt(initial_prompt)
|
|
425
454
|
launch = session.get("launch") or {}
|
|
426
455
|
power = _launch_power(session)
|
|
427
456
|
permission = launch.get("permission")
|
package/src/session_service.py
CHANGED
|
@@ -149,6 +149,18 @@ def _normalize_launch_settings(settings):
|
|
|
149
149
|
normalized["rtk"] = False
|
|
150
150
|
else:
|
|
151
151
|
raise CdxError(f"Unsupported rtk value: {settings['rtk']}")
|
|
152
|
+
if "logics" in settings and settings["logics"] is not None:
|
|
153
|
+
value = settings["logics"]
|
|
154
|
+
if isinstance(value, bool):
|
|
155
|
+
normalized["logics"] = value
|
|
156
|
+
else:
|
|
157
|
+
text = str(value).strip().lower()
|
|
158
|
+
if text in ("on", "true", "1", "yes"):
|
|
159
|
+
normalized["logics"] = True
|
|
160
|
+
elif text in ("off", "false", "0", "no"):
|
|
161
|
+
normalized["logics"] = False
|
|
162
|
+
else:
|
|
163
|
+
raise CdxError(f"Unsupported logics value: {settings['logics']}")
|
|
152
164
|
if "model" in settings and settings["model"] is not None:
|
|
153
165
|
model = str(settings["model"]).strip()
|
|
154
166
|
if not model:
|
|
@@ -839,7 +851,7 @@ def create_session_service(options=None):
|
|
|
839
851
|
raise CdxError(f"Unknown session: {name}")
|
|
840
852
|
if not keys:
|
|
841
853
|
raise CdxError("At least one launch setting is required.")
|
|
842
|
-
allowed = {"power", "permission", "fast", "rtk", "model", "priority"}
|
|
854
|
+
allowed = {"power", "permission", "fast", "rtk", "logics", "model", "priority"}
|
|
843
855
|
unknown = [key for key in keys if key not in allowed]
|
|
844
856
|
if unknown:
|
|
845
857
|
raise CdxError(f"Unsupported launch setting: {', '.join(unknown)}")
|
package/src/update_check.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
3
5
|
import urllib.error
|
|
4
6
|
import urllib.request
|
|
5
7
|
from datetime import datetime, timezone
|
|
@@ -7,6 +9,7 @@ from datetime import datetime, timezone
|
|
|
7
9
|
|
|
8
10
|
UPDATE_CHECK_TTL_SECONDS = 12 * 60 * 60
|
|
9
11
|
LATEST_RELEASE_URL = "https://api.github.com/repos/AlexAgo83/cdx-manager/releases/latest"
|
|
12
|
+
LOGICS_MANAGER_LATEST_URL = "https://registry.npmjs.org/@grifhinz%2Flogics-manager/latest"
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class LatestReleaseCheckError(Exception):
|
|
@@ -40,6 +43,10 @@ def _cache_path(base_dir):
|
|
|
40
43
|
return os.path.join(base_dir, "state", "update-check.json")
|
|
41
44
|
|
|
42
45
|
|
|
46
|
+
def _tool_cache_path(base_dir, tool):
|
|
47
|
+
return os.path.join(base_dir, "state", f"{tool}-update-check.json")
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
def _read_cache(path):
|
|
44
51
|
try:
|
|
45
52
|
with open(path, "r", encoding="utf-8") as handle:
|
|
@@ -106,6 +113,50 @@ def fetch_latest_release(env=None):
|
|
|
106
113
|
return None
|
|
107
114
|
|
|
108
115
|
|
|
116
|
+
def _fetch_latest_npm_package_version(url=LOGICS_MANAGER_LATEST_URL):
|
|
117
|
+
request = urllib.request.Request(
|
|
118
|
+
url,
|
|
119
|
+
headers={
|
|
120
|
+
"Accept": "application/json",
|
|
121
|
+
"User-Agent": "cdx-manager-update-check",
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
with urllib.request.urlopen(request, timeout=1) as response:
|
|
125
|
+
payload = json.loads(response.read().decode("utf-8"))
|
|
126
|
+
version = payload.get("version") if isinstance(payload, dict) else None
|
|
127
|
+
return str(version).strip() if version else None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def fetch_latest_logics_manager_version(env=None):
|
|
131
|
+
try:
|
|
132
|
+
return _fetch_latest_npm_package_version()
|
|
133
|
+
except (urllib.error.URLError, TimeoutError, ValueError, OSError):
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _installed_logics_manager_version(env=None, runner=None):
|
|
138
|
+
env = env or os.environ
|
|
139
|
+
executable = shutil.which("logics-manager", path=env.get("PATH", ""))
|
|
140
|
+
if not executable:
|
|
141
|
+
return None
|
|
142
|
+
runner = runner or subprocess.run
|
|
143
|
+
try:
|
|
144
|
+
result = runner(
|
|
145
|
+
[executable, "--version"],
|
|
146
|
+
capture_output=True,
|
|
147
|
+
text=True,
|
|
148
|
+
timeout=2,
|
|
149
|
+
env=env,
|
|
150
|
+
)
|
|
151
|
+
except (OSError, subprocess.SubprocessError):
|
|
152
|
+
return None
|
|
153
|
+
if getattr(result, "returncode", 0) not in (0, None):
|
|
154
|
+
return None
|
|
155
|
+
output = (getattr(result, "stdout", "") or getattr(result, "stderr", "") or "").strip()
|
|
156
|
+
parts = output.split()
|
|
157
|
+
return parts[-1].lstrip("v") if parts else None
|
|
158
|
+
|
|
159
|
+
|
|
109
160
|
def fetch_latest_release_or_raise(env=None):
|
|
110
161
|
try:
|
|
111
162
|
return _fetch_latest_release(env=env)
|
|
@@ -154,3 +205,55 @@ def check_for_update(base_dir, current_version, env=None, now_fn=None):
|
|
|
154
205
|
"cached": False,
|
|
155
206
|
}
|
|
156
207
|
return None
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def check_logics_manager_for_update(base_dir, env=None, now_fn=None, runner=None):
|
|
211
|
+
env = env or os.environ
|
|
212
|
+
now_fn = now_fn or (lambda: datetime.now(timezone.utc).timestamp())
|
|
213
|
+
if env.get("CDX_DISABLE_UPDATE_CHECK") in {"1", "true", "TRUE", "yes", "YES"}:
|
|
214
|
+
return None
|
|
215
|
+
if env.get("LOGICS_MANAGER_NO_UPDATE_CHECK") in {"1", "true", "TRUE", "yes", "YES"}:
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
current_version = _installed_logics_manager_version(env=env, runner=runner)
|
|
219
|
+
if not current_version:
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
path = _tool_cache_path(base_dir, "logics-manager")
|
|
223
|
+
now_ts = float(now_fn())
|
|
224
|
+
cached = _read_cache(path) or {}
|
|
225
|
+
checked_at = cached.get("checked_at")
|
|
226
|
+
if isinstance(checked_at, (int, float)) and (now_ts - checked_at) < UPDATE_CHECK_TTL_SECONDS:
|
|
227
|
+
latest_version = cached.get("latest_version")
|
|
228
|
+
if _is_newer_version(current_version, latest_version):
|
|
229
|
+
return {
|
|
230
|
+
"tool": "logics-manager",
|
|
231
|
+
"latest_version": latest_version,
|
|
232
|
+
"current_version": current_version,
|
|
233
|
+
"update_command": "logics-manager self-update",
|
|
234
|
+
"url": cached.get("url"),
|
|
235
|
+
"cached": True,
|
|
236
|
+
}
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
latest_version = fetch_latest_logics_manager_version(env=env)
|
|
240
|
+
payload = {
|
|
241
|
+
"checked_at": now_ts,
|
|
242
|
+
"latest_version": latest_version,
|
|
243
|
+
"url": "https://www.npmjs.com/package/@grifhinz/logics-manager",
|
|
244
|
+
}
|
|
245
|
+
try:
|
|
246
|
+
_write_cache(path, payload)
|
|
247
|
+
except OSError:
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
if _is_newer_version(current_version, latest_version):
|
|
251
|
+
return {
|
|
252
|
+
"tool": "logics-manager",
|
|
253
|
+
"latest_version": latest_version,
|
|
254
|
+
"current_version": current_version,
|
|
255
|
+
"update_command": "logics-manager self-update",
|
|
256
|
+
"url": payload["url"],
|
|
257
|
+
"cached": False,
|
|
258
|
+
}
|
|
259
|
+
return None
|