cdx-manager 0.9.16 → 0.11.0
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 +80 -10
- package/bin/cdx +2 -11
- package/changelogs/CHANGELOGS_0_10_0.md +43 -0
- package/changelogs/CHANGELOGS_0_11_0.md +51 -0
- package/checksums/release-archives.json +8 -0
- package/install.ps1 +12 -19
- package/install.sh +6 -29
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/claude_refresh.py +17 -33
- package/src/cli.py +163 -28
- package/src/cli_args.py +15 -12
- package/src/cli_commands.py +531 -9
- package/src/cli_helpers.py +33 -38
- package/src/cli_view.py +5 -27
- package/src/codex_usage.py +95 -24
- package/src/notify.py +6 -10
- package/src/provider_runtime.py +58 -14
- package/src/session_service.py +141 -57
- package/src/session_store.py +2 -2
- package/src/status_source.py +47 -25
- package/src/status_view.py +14 -6
package/src/cli.py
CHANGED
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
+
from datetime import datetime, timezone
|
|
6
7
|
|
|
7
8
|
from .cli_commands import (
|
|
8
9
|
API_SCHEMA_VERSION,
|
|
9
10
|
STATUS_USAGE,
|
|
11
|
+
_collect_profile_cleanup_candidates,
|
|
12
|
+
_directory_child_sizes,
|
|
13
|
+
_directory_size_bytes,
|
|
10
14
|
handle_add,
|
|
11
15
|
handle_can_resume,
|
|
12
16
|
handle_clean,
|
|
@@ -15,6 +19,7 @@ from .cli_commands import (
|
|
|
15
19
|
handle_context,
|
|
16
20
|
handle_copy,
|
|
17
21
|
handle_disable,
|
|
22
|
+
handle_disk,
|
|
18
23
|
handle_doctor,
|
|
19
24
|
handle_enable,
|
|
20
25
|
handle_export,
|
|
@@ -31,6 +36,7 @@ from .cli_commands import (
|
|
|
31
36
|
handle_remove,
|
|
32
37
|
handle_rename,
|
|
33
38
|
handle_repair,
|
|
39
|
+
handle_reset,
|
|
34
40
|
handle_resume,
|
|
35
41
|
handle_run,
|
|
36
42
|
handle_run_report,
|
|
@@ -44,6 +50,10 @@ from .cli_commands import (
|
|
|
44
50
|
handle_update,
|
|
45
51
|
handle_view,
|
|
46
52
|
)
|
|
53
|
+
from .cli_commands import (
|
|
54
|
+
_format_bytes as _format_disk_bytes,
|
|
55
|
+
)
|
|
56
|
+
from .cli_helpers import _update_notice_warnings
|
|
47
57
|
from .cli_render import (
|
|
48
58
|
_format_sessions,
|
|
49
59
|
_pad_table,
|
|
@@ -66,7 +76,7 @@ from .status_view import (
|
|
|
66
76
|
)
|
|
67
77
|
from .update_check import check_for_update, check_logics_manager_for_update
|
|
68
78
|
|
|
69
|
-
VERSION = "0.
|
|
79
|
+
VERSION = "0.11.0"
|
|
70
80
|
|
|
71
81
|
# Public surface: this module is a facade. Names below are imported above
|
|
72
82
|
# purely to be re-exported (consumed by tests and external callers); listing
|
|
@@ -94,6 +104,7 @@ _COMMAND_HANDLERS = {
|
|
|
94
104
|
"config": handle_config,
|
|
95
105
|
"configs": handle_configs,
|
|
96
106
|
"context": handle_context,
|
|
107
|
+
"disk": handle_disk,
|
|
97
108
|
"doctor": handle_doctor,
|
|
98
109
|
"disable": handle_disable,
|
|
99
110
|
"enable": handle_enable,
|
|
@@ -107,6 +118,7 @@ _COMMAND_HANDLERS = {
|
|
|
107
118
|
"next": handle_next,
|
|
108
119
|
"notify": handle_notify,
|
|
109
120
|
"repair": handle_repair,
|
|
121
|
+
"reset": handle_reset,
|
|
110
122
|
"rename": handle_rename,
|
|
111
123
|
"resume": handle_resume,
|
|
112
124
|
"can-resume": handle_can_resume,
|
|
@@ -165,12 +177,14 @@ def _print_help(use_color=False):
|
|
|
165
177
|
f" {_style('cdx status --small|-s [--refresh|--cached] [--timeout SECONDS]', '36', use_color)}",
|
|
166
178
|
f" {_style('cdx status <name> [--json] [--refresh|--cached] [--timeout SECONDS]', '36', use_color)}",
|
|
167
179
|
f" {_style('cdx next [--json] [--refresh]', '36', use_color)}",
|
|
180
|
+
f" {_style('cdx reset <name> [--yes] [--json]', '36', use_color)}",
|
|
168
181
|
f" {_style('cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json', '36', use_color)}",
|
|
169
|
-
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] --json', '36', use_color)}",
|
|
182
|
+
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] --json', '36', use_color)}",
|
|
170
183
|
f" {_style('cdx runs [--limit N] --json', '36', use_color)}",
|
|
171
184
|
f" {_style('cdx run-status <run_id> --json', '36', use_color)}",
|
|
172
185
|
f" {_style('cdx run-report <run_id> --json', '36', use_color)}",
|
|
173
186
|
f" {_style('cdx context show|path|init|edit|clear|set [text...] [--json]', '36', use_color)}",
|
|
187
|
+
f" {_style('cdx disk [profiles] [--candidates] [--json]', '36', use_color)}",
|
|
174
188
|
f" {_style('cdx config <name> [--json]', '36', use_color)}",
|
|
175
189
|
f" {_style('cdx configs [--json]', '36', use_color)}",
|
|
176
190
|
f" {_style('cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]', '36', use_color)}",
|
|
@@ -191,7 +205,8 @@ def _print_help(use_color=False):
|
|
|
191
205
|
f" {_style('cdx disable <name> [--json]', '36', use_color)}",
|
|
192
206
|
f" {_style('cdx enable <name> [--json]', '36', use_color)}",
|
|
193
207
|
f" {_style('cdx rmv <name> [--force] [--json]', '36', use_color)}",
|
|
194
|
-
f" {_style('cdx clean [name] [--json]', '36', use_color)}",
|
|
208
|
+
f" {_style('cdx clean [name] [--yes] [--json]', '36', use_color)}",
|
|
209
|
+
f" {_style('cdx clean profiles (--tmp|--old-logs DAYS) [--yes] [--json]', '36', use_color)}",
|
|
195
210
|
f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
|
|
196
211
|
f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--json]', '36', use_color)}",
|
|
197
212
|
f" {_style('cdx doctor [--json]', '36', use_color)}",
|
|
@@ -264,36 +279,156 @@ def _get_update_notices(service, env, options):
|
|
|
264
279
|
)
|
|
265
280
|
if logics_notice:
|
|
266
281
|
notices.append(logics_notice)
|
|
282
|
+
disk_notice = _get_disk_cleanup_notice(service, options)
|
|
283
|
+
if disk_notice:
|
|
284
|
+
notices.append(disk_notice)
|
|
267
285
|
return notices
|
|
268
286
|
|
|
269
287
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
_DISK_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
|
|
289
|
+
_DISK_HOME_THRESHOLD_BYTES = 10 * 1024 * 1024 * 1024
|
|
290
|
+
_DISK_RECLAIMABLE_THRESHOLD_BYTES = 1 * 1024 * 1024 * 1024
|
|
291
|
+
_DISK_CATEGORY_THRESHOLD_BYTES = 500 * 1024 * 1024
|
|
292
|
+
_DISK_PROFILE_THRESHOLD_BYTES = 2 * 1024 * 1024 * 1024
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _now_utc(options):
|
|
296
|
+
now_fn = options.get("now")
|
|
297
|
+
value = now_fn() if callable(now_fn) else None
|
|
298
|
+
if isinstance(value, datetime):
|
|
299
|
+
return value.astimezone(timezone.utc)
|
|
300
|
+
return datetime.now(timezone.utc)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _read_json_file(path):
|
|
304
|
+
try:
|
|
305
|
+
with open(path, encoding="utf-8") as handle:
|
|
306
|
+
return json.load(handle)
|
|
307
|
+
except (OSError, ValueError):
|
|
308
|
+
return {}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _write_json_file(path, payload):
|
|
312
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
313
|
+
tmp_path = f"{path}.tmp"
|
|
314
|
+
with open(tmp_path, "w", encoding="utf-8") as handle:
|
|
315
|
+
json.dump(payload, handle, indent=2, sort_keys=True)
|
|
316
|
+
handle.write("\n")
|
|
317
|
+
os.replace(tmp_path, path)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _parse_iso_datetime(value):
|
|
321
|
+
if not value:
|
|
322
|
+
return None
|
|
323
|
+
try:
|
|
324
|
+
return datetime.fromisoformat(str(value).replace("Z", "+00:00")).astimezone(timezone.utc)
|
|
325
|
+
except ValueError:
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _get_disk_cleanup_notice(service, options):
|
|
330
|
+
checker = options.get("checkDiskCleanup")
|
|
331
|
+
if callable(checker):
|
|
332
|
+
return checker(service, options)
|
|
333
|
+
|
|
334
|
+
base_dir = service["base_dir"]
|
|
335
|
+
state_path = os.path.join(base_dir, "state", "disk-cleanup-check.json")
|
|
336
|
+
now = _now_utc(options)
|
|
337
|
+
state = _read_json_file(state_path)
|
|
338
|
+
last_checked = _parse_iso_datetime(state.get("last_checked_at"))
|
|
339
|
+
if last_checked and (now - last_checked).total_seconds() < _DISK_CHECK_INTERVAL_SECONDS:
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
runner = options.get("diskUsageRunner")
|
|
343
|
+
try:
|
|
344
|
+
home_bytes = _directory_size_bytes(base_dir, runner=runner)
|
|
345
|
+
profiles_dir = os.path.join(base_dir, "profiles")
|
|
346
|
+
profile_rows = _directory_child_sizes(profiles_dir, runner=runner)
|
|
347
|
+
profile_total = sum(row["bytes"] for row in profile_rows)
|
|
348
|
+
candidates = _collect_profile_cleanup_candidates(base_dir, runner=runner, now=now.timestamp())
|
|
349
|
+
except Exception:
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
reclaimable_bytes = sum(item["bytes"] for item in candidates)
|
|
353
|
+
tmp_bytes = sum(item["bytes"] for item in candidates if item["kind"].startswith("tmp-"))
|
|
354
|
+
old_log_bytes = sum(item["bytes"] for item in candidates if item["kind"].startswith("old-logs-"))
|
|
355
|
+
large_profiles = [
|
|
356
|
+
{
|
|
357
|
+
"name": row["name"],
|
|
358
|
+
"bytes": row["bytes"],
|
|
359
|
+
"size": _format_disk_bytes(row["bytes"]),
|
|
360
|
+
"share_pct": round((row["bytes"] / profile_total) * 100, 1) if profile_total else 0,
|
|
361
|
+
}
|
|
362
|
+
for row in profile_rows
|
|
363
|
+
if row["bytes"] >= _DISK_PROFILE_THRESHOLD_BYTES
|
|
364
|
+
or (
|
|
365
|
+
profile_total >= _DISK_RECLAIMABLE_THRESHOLD_BYTES
|
|
366
|
+
and row["bytes"] / profile_total >= 0.25
|
|
367
|
+
)
|
|
368
|
+
]
|
|
369
|
+
triggered = (
|
|
370
|
+
home_bytes >= _DISK_HOME_THRESHOLD_BYTES
|
|
371
|
+
or reclaimable_bytes >= _DISK_RECLAIMABLE_THRESHOLD_BYTES
|
|
372
|
+
or tmp_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES
|
|
373
|
+
or old_log_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES
|
|
374
|
+
or bool(large_profiles)
|
|
375
|
+
)
|
|
376
|
+
state_payload = {
|
|
377
|
+
"last_checked_at": now.isoformat(),
|
|
378
|
+
"home_bytes": home_bytes,
|
|
379
|
+
"reclaimable_bytes": reclaimable_bytes,
|
|
380
|
+
"tmp_bytes": tmp_bytes,
|
|
381
|
+
"old_log_bytes": old_log_bytes,
|
|
382
|
+
"large_profiles": large_profiles,
|
|
383
|
+
}
|
|
384
|
+
try:
|
|
385
|
+
_write_json_file(state_path, state_payload)
|
|
386
|
+
except OSError:
|
|
387
|
+
pass
|
|
388
|
+
if not triggered:
|
|
389
|
+
return None
|
|
390
|
+
|
|
391
|
+
if reclaimable_bytes > 0:
|
|
392
|
+
message = (
|
|
393
|
+
f"Disk cleanup available: {_format_disk_bytes(reclaimable_bytes)} reclaimable "
|
|
394
|
+
f"in CDX profiles. Inspect: cdx disk profiles --candidates"
|
|
395
|
+
)
|
|
396
|
+
else:
|
|
397
|
+
message = (
|
|
398
|
+
f"CDX_HOME uses {_format_disk_bytes(home_bytes)}. "
|
|
399
|
+
"Inspect: cdx disk profiles --candidates"
|
|
400
|
+
)
|
|
401
|
+
if tmp_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES:
|
|
402
|
+
message = (
|
|
403
|
+
f"{message}. Safe temp cleanup: {_format_disk_bytes(tmp_bytes)} "
|
|
404
|
+
"with cdx clean profiles --tmp"
|
|
405
|
+
)
|
|
406
|
+
if old_log_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES:
|
|
407
|
+
message = (
|
|
408
|
+
f"{message}. Old logs: {_format_disk_bytes(old_log_bytes)} "
|
|
409
|
+
"with cdx clean profiles --old-logs 30d"
|
|
410
|
+
)
|
|
411
|
+
return {
|
|
412
|
+
"tool": "cdx-disk",
|
|
413
|
+
"code": "disk_cleanup_available",
|
|
414
|
+
"message": message,
|
|
415
|
+
"reclaimable_bytes": reclaimable_bytes,
|
|
416
|
+
"reclaimable_size": _format_disk_bytes(reclaimable_bytes),
|
|
417
|
+
"home_bytes": home_bytes,
|
|
418
|
+
"home_size": _format_disk_bytes(home_bytes),
|
|
419
|
+
"tmp_bytes": tmp_bytes,
|
|
420
|
+
"tmp_size": _format_disk_bytes(tmp_bytes),
|
|
421
|
+
"old_log_bytes": old_log_bytes,
|
|
422
|
+
"old_log_size": _format_disk_bytes(old_log_bytes),
|
|
423
|
+
"large_profiles": large_profiles,
|
|
424
|
+
"inspect_command": "cdx disk profiles --candidates",
|
|
425
|
+
"cleanup_tmp_command": "cdx clean profiles --tmp" if tmp_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES else None,
|
|
426
|
+
"cleanup_old_logs_command": "cdx clean profiles --old-logs 30d" if old_log_bytes >= _DISK_CATEGORY_THRESHOLD_BYTES else None,
|
|
427
|
+
}
|
|
293
428
|
|
|
294
429
|
|
|
295
430
|
def _update_warning_text(notices):
|
|
296
|
-
payloads =
|
|
431
|
+
payloads = _update_notice_warnings({"update_notices": notices or [], "version": VERSION})
|
|
297
432
|
if not payloads:
|
|
298
433
|
return None
|
|
299
434
|
return "\n".join(payload["message"] for payload in payloads)
|
|
@@ -405,7 +540,7 @@ def _list_json_payload(rows, notices=None):
|
|
|
405
540
|
"ok": True,
|
|
406
541
|
"action": "list",
|
|
407
542
|
"message": "Listed known sessions",
|
|
408
|
-
"warnings":
|
|
543
|
+
"warnings": _update_notice_warnings({"update_notices": notices or [], "version": VERSION}),
|
|
409
544
|
"sessions": rows,
|
|
410
545
|
}
|
|
411
546
|
|
package/src/cli_args.py
CHANGED
|
@@ -13,14 +13,15 @@ from .provider_runtime import _normalize_reasoning_effort
|
|
|
13
13
|
|
|
14
14
|
STATUS_USAGE = "Usage: cdx status [--json] [--refresh|--cached] [--timeout SECONDS] | cdx status --small|-s [--refresh|--cached] [--timeout SECONDS] | cdx status <name> [--json] [--refresh|--cached] [--timeout SECONDS]"
|
|
15
15
|
DOCTOR_USAGE = "Usage: cdx doctor [--json]"
|
|
16
|
+
DISK_USAGE = "Usage: cdx disk [profiles] [--candidates] [--json]"
|
|
16
17
|
REPAIR_USAGE = "Usage: cdx repair [--dry-run] [--force] [--json]"
|
|
17
18
|
UPDATE_USAGE = "Usage: cdx update [--check] [--yes] [--json] [--version TAG]"
|
|
18
19
|
EXPORT_USAGE = "Usage: cdx export <file> [--include-auth] [--force] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
|
|
19
|
-
IMPORT_USAGE = "Usage: cdx import <file> [--force|--merge] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
|
|
20
|
+
IMPORT_USAGE = "Usage: cdx import <file> [--force|--merge] [--allow-authless-force] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
|
|
20
21
|
CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set [text...] [--json]"
|
|
21
22
|
HANDOFF_USAGE = "Usage: cdx handoff <name> [--json] | cdx handoff <source> <target> [--json]"
|
|
22
23
|
SET_USAGE = "Usage: cdx set <name>|--sessions all|a,b|--provider PROVIDER [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full] [--fast on|off] [--rtk on|off] [--logics on|off] [--model MODEL] [--priority 0..100] [--json]"
|
|
23
|
-
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--permission|--fast|--rtk|--logics|--model|--priority|--all) [--json]"
|
|
24
|
+
UNSET_USAGE = "Usage: cdx unset <name>|--sessions all|a,b|--provider PROVIDER (--power|--reasoning-effort|--permission|--fast|--rtk|--logics|--model|--priority|--all) [--json]"
|
|
24
25
|
SETTING_ALIAS_USAGE = "Usage: cdx power|perm|fast|model <name|all|provider:PROVIDER|a,b> <value|default> [--json]"
|
|
25
26
|
CONFIG_USAGE = "Usage: cdx config <name> [--json]"
|
|
26
27
|
CONFIGS_USAGE = "Usage: cdx configs [--json]"
|
|
@@ -185,6 +186,7 @@ def _parse_set_args(args):
|
|
|
185
186
|
def _parse_unset_args(args):
|
|
186
187
|
parsed = _parse_flag_args(args, {
|
|
187
188
|
"--power": {"key": "power", "type": "bool", "default": False},
|
|
189
|
+
"--reasoning-effort": {"key": "reasoning_effort", "type": "bool", "default": False},
|
|
188
190
|
"--permission": {"key": "permission", "type": "bool", "default": False},
|
|
189
191
|
"--fast": {"key": "fast", "type": "bool", "default": False},
|
|
190
192
|
"--rtk": {"key": "rtk", "type": "bool", "default": False},
|
|
@@ -206,8 +208,8 @@ def _parse_unset_args(args):
|
|
|
206
208
|
raise CdxError(UNSET_USAGE)
|
|
207
209
|
if not parsed["names"] and not parsed["sessions"] and not parsed["provider"]:
|
|
208
210
|
raise CdxError(UNSET_USAGE)
|
|
209
|
-
keys = ["power", "permission", "fast", "rtk", "logics", "model", "priority"] if parsed["all"] else [
|
|
210
|
-
key for key in ("power", "permission", "fast", "rtk", "logics", "model", "priority") if parsed[key]
|
|
211
|
+
keys = ["power", "reasoning_effort", "permission", "fast", "rtk", "logics", "model", "priority"] if parsed["all"] else [
|
|
212
|
+
key for key in ("power", "reasoning_effort", "permission", "fast", "rtk", "logics", "model", "priority") if parsed[key]
|
|
211
213
|
]
|
|
212
214
|
if not keys:
|
|
213
215
|
raise CdxError(UNSET_USAGE)
|
|
@@ -404,18 +406,18 @@ def _format_period_datetime(value):
|
|
|
404
406
|
return value.isoformat(timespec="seconds")
|
|
405
407
|
|
|
406
408
|
|
|
407
|
-
def _parse_history_period(parsed, now):
|
|
409
|
+
def _parse_history_period(parsed, now, usage=HISTORY_USAGE):
|
|
408
410
|
since = parsed.get("since")
|
|
409
411
|
from_value = parsed.get("from")
|
|
410
412
|
to_value = parsed.get("to")
|
|
411
413
|
if since and from_value:
|
|
412
|
-
raise CdxError("
|
|
413
|
-
start = _parse_since_value(since, now,
|
|
414
|
+
raise CdxError(f"{usage} cannot combine --since and --from.")
|
|
415
|
+
start = _parse_since_value(since, now, usage) if since else None
|
|
414
416
|
if from_value:
|
|
415
|
-
start = _parse_datetime_value(from_value,
|
|
416
|
-
end = _parse_datetime_value(to_value,
|
|
417
|
+
start = _parse_datetime_value(from_value, usage, end_of_day=False)
|
|
418
|
+
end = _parse_datetime_value(to_value, usage, end_of_day=True) if to_value else None
|
|
417
419
|
if start and end and start.timestamp() > end.timestamp():
|
|
418
|
-
raise CdxError("
|
|
420
|
+
raise CdxError(f"{usage} period start must be before period end.")
|
|
419
421
|
return {
|
|
420
422
|
"from": _format_period_datetime(start),
|
|
421
423
|
"to": _format_period_datetime(end),
|
|
@@ -479,7 +481,7 @@ def _parse_history_args(args, now=None):
|
|
|
479
481
|
"--to": {"key": "to", "type": "str", "default": None},
|
|
480
482
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
481
483
|
}, HISTORY_USAGE, positionals_key="names", max_positionals=1)
|
|
482
|
-
period = _parse_history_period(parsed, now)
|
|
484
|
+
period = _parse_history_period(parsed, now, HISTORY_USAGE)
|
|
483
485
|
return {
|
|
484
486
|
"name": parsed["names"][0] if parsed["names"] else None,
|
|
485
487
|
"limit": parsed["limit"],
|
|
@@ -497,7 +499,7 @@ def _parse_stats_args(args, now=None):
|
|
|
497
499
|
"--to": {"key": "to", "type": "str", "default": None},
|
|
498
500
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
499
501
|
}, STATS_USAGE, positionals_key="names", max_positionals=1)
|
|
500
|
-
period = _parse_history_period(parsed, now)
|
|
502
|
+
period = _parse_history_period(parsed, now, STATS_USAGE)
|
|
501
503
|
return {
|
|
502
504
|
"name": parsed["names"][0] if parsed["names"] else None,
|
|
503
505
|
"period": period,
|
|
@@ -611,6 +613,7 @@ def _parse_import_args(args):
|
|
|
611
613
|
parsed = _parse_flag_args(args, {
|
|
612
614
|
"--force": {"key": "force", "type": "bool", "default": False},
|
|
613
615
|
"--merge": {"key": "merge", "type": "bool", "default": False},
|
|
616
|
+
"--allow-authless-force": {"key": "allow_authless_force", "type": "bool", "default": False},
|
|
614
617
|
"--json": {"key": "json", "type": "bool", "default": False},
|
|
615
618
|
"--sessions": {
|
|
616
619
|
"key": "session_names",
|