forgexa-cli 1.4.2__tar.gz → 1.5.2__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.
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/PKG-INFO +1 -1
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli/daemon.py +54 -27
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli/main.py +139 -25
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/pyproject.toml +1 -1
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/README.md +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.4.2 → forgexa_cli-1.5.2}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.5.2"
|
|
@@ -279,6 +279,10 @@ except (ImportError, ModuleNotFoundError):
|
|
|
279
279
|
def AGENT_TIMEOUT(self) -> int:
|
|
280
280
|
return int(os.environ.get("AGENT_TIMEOUT", "3600"))
|
|
281
281
|
|
|
282
|
+
@property
|
|
283
|
+
def GIT_CLONE_TIMEOUT(self) -> int:
|
|
284
|
+
return int(os.environ.get("GIT_CLONE_TIMEOUT", "600"))
|
|
285
|
+
|
|
282
286
|
@property
|
|
283
287
|
def AGENT_MAX_OUTPUT_SIZE(self) -> int:
|
|
284
288
|
return int(os.environ.get("AGENT_MAX_OUTPUT_SIZE", "100000"))
|
|
@@ -303,7 +307,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
303
307
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
304
308
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
305
309
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
306
|
-
DAEMON_VERSION = "1.
|
|
310
|
+
DAEMON_VERSION = "1.5.2"
|
|
307
311
|
|
|
308
312
|
|
|
309
313
|
def _detect_client_type() -> str:
|
|
@@ -1085,7 +1089,10 @@ class WorkspaceManager:
|
|
|
1085
1089
|
|
|
1086
1090
|
# Ensure _main repo is present and up-to-date
|
|
1087
1091
|
if not main_repo.exists():
|
|
1088
|
-
await self._git(
|
|
1092
|
+
await self._git(
|
|
1093
|
+
"clone", "--single-branch", "--no-tags",
|
|
1094
|
+
repo_url, str(main_repo), timeout=settings.GIT_CLONE_TIMEOUT, project_key=project_key,
|
|
1095
|
+
)
|
|
1089
1096
|
else:
|
|
1090
1097
|
await self._git("fetch", "--all", cwd=main_repo, timeout=300, project_key=project_key)
|
|
1091
1098
|
|
|
@@ -1173,7 +1180,10 @@ class WorkspaceManager:
|
|
|
1173
1180
|
)
|
|
1174
1181
|
except Exception:
|
|
1175
1182
|
ws_path.mkdir(parents=True, exist_ok=True)
|
|
1176
|
-
await self._git(
|
|
1183
|
+
await self._git(
|
|
1184
|
+
"clone", "--single-branch", "--no-tags",
|
|
1185
|
+
repo_url, str(ws_path), timeout=settings.GIT_CLONE_TIMEOUT, project_key=project_key,
|
|
1186
|
+
)
|
|
1177
1187
|
# Ensure we're on the correct branch after clone
|
|
1178
1188
|
try:
|
|
1179
1189
|
await self._git("checkout", "-B", branch_name, cwd=ws_path)
|
|
@@ -1195,7 +1205,10 @@ class WorkspaceManager:
|
|
|
1195
1205
|
except Exception:
|
|
1196
1206
|
# Fallback to simple clone
|
|
1197
1207
|
ws_path.mkdir(parents=True, exist_ok=True)
|
|
1198
|
-
await self._git(
|
|
1208
|
+
await self._git(
|
|
1209
|
+
"clone", "--single-branch", "--no-tags",
|
|
1210
|
+
repo_url, str(ws_path), timeout=settings.GIT_CLONE_TIMEOUT, project_key=project_key,
|
|
1211
|
+
)
|
|
1199
1212
|
# Ensure we're on the correct branch after clone
|
|
1200
1213
|
try:
|
|
1201
1214
|
await self._git("checkout", "-B", branch_name, cwd=ws_path)
|
|
@@ -1422,21 +1435,29 @@ class ProcessManager:
|
|
|
1422
1435
|
Returns True for rate/quota limits AND API unavailability errors,
|
|
1423
1436
|
since a different agent (using a different API backend) may succeed.
|
|
1424
1437
|
|
|
1425
|
-
IMPORTANT: Only checks stderr
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1438
|
+
IMPORTANT: Only checks stderr and error message. When exit code is
|
|
1439
|
+
non-zero, also checks the tail of stdout (last 3000 chars) since the
|
|
1440
|
+
error is likely at the end. When exit code is 0 (agent reported
|
|
1441
|
+
success but _detect_agent_output_failure set status to failed), do
|
|
1442
|
+
NOT scan stdout — it contains the agent's work output (configs, code)
|
|
1443
|
+
which naturally has terms like "rate_limit", "API_RATE_LIMIT_PER_MINUTE"
|
|
1444
|
+
that trigger false positives.
|
|
1430
1445
|
"""
|
|
1431
1446
|
if result.status == "success":
|
|
1432
1447
|
return False
|
|
1433
|
-
#
|
|
1434
|
-
#
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1448
|
+
# When exit code is 0, _detect_agent_output_failure already checked
|
|
1449
|
+
# stderr+error for rate-limit patterns. Don't re-scan stdout here.
|
|
1450
|
+
if result.exit_code == 0:
|
|
1451
|
+
error_text = (
|
|
1452
|
+
(result.stderr or "")
|
|
1453
|
+
+ "\n" + (result.error or "")
|
|
1454
|
+
).lower()
|
|
1455
|
+
else:
|
|
1456
|
+
error_text = (
|
|
1457
|
+
(result.stderr or "")
|
|
1458
|
+
+ "\n" + (result.error or "")
|
|
1459
|
+
+ "\n" + (result.stdout or "")[-3000:]
|
|
1460
|
+
).lower()
|
|
1440
1461
|
return (
|
|
1441
1462
|
any(p in error_text for p in ProcessManager.RATE_LIMIT_PATTERNS)
|
|
1442
1463
|
or any(p in error_text for p in ProcessManager.AGENT_UNAVAILABLE_PATTERNS)
|
|
@@ -1456,16 +1477,16 @@ class ProcessManager:
|
|
|
1456
1477
|
if result.status != "success":
|
|
1457
1478
|
return None
|
|
1458
1479
|
|
|
1459
|
-
# For
|
|
1460
|
-
#
|
|
1461
|
-
#
|
|
1462
|
-
#
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
)
|
|
1468
|
-
pattern_failure = ProcessManager._has_failure_pattern(
|
|
1480
|
+
# For exit-code-0 (success) cases, only scan stderr and the error field
|
|
1481
|
+
# for rate-limit / unavailability patterns. Stdout contains the agent's
|
|
1482
|
+
# actual task output (code, configs, analysis docs) which may legitimately
|
|
1483
|
+
# contain substrings like "rate_limit", "429", "quota", etc. — e.g. writing
|
|
1484
|
+
# a config file with API_RATE_LIMIT_PER_MINUTE=1000 would previously trigger
|
|
1485
|
+
# a false "quota exhaustion" failure even though the agent succeeded.
|
|
1486
|
+
# stdout[-N:] is only safe to scan when the agent already failed (exit != 0),
|
|
1487
|
+
# which is handled by is_rate_limited() called at the orchestrator level.
|
|
1488
|
+
error_only_channels = (result.stderr or "") + "\n" + (result.error or "")
|
|
1489
|
+
pattern_failure = ProcessManager._has_failure_pattern(error_only_channels)
|
|
1469
1490
|
if pattern_failure:
|
|
1470
1491
|
return pattern_failure
|
|
1471
1492
|
|
|
@@ -1849,7 +1870,13 @@ class ProcessManager:
|
|
|
1849
1870
|
on_chunk: Any = None,
|
|
1850
1871
|
) -> TaskResult:
|
|
1851
1872
|
"""Run OpenCode CLI in non-interactive mode."""
|
|
1852
|
-
cmd = [
|
|
1873
|
+
cmd = [
|
|
1874
|
+
agent.command, "run",
|
|
1875
|
+
"--format", "json",
|
|
1876
|
+
"--dangerously-skip-permissions",
|
|
1877
|
+
"--cwd", str(cwd),
|
|
1878
|
+
prompt,
|
|
1879
|
+
]
|
|
1853
1880
|
result = await self._run_cli(cmd, cwd, timeout, task_id, on_chunk=on_chunk)
|
|
1854
1881
|
parsed_metrics = self._parse_agent_jsonl_output(result.stdout)
|
|
1855
1882
|
result.metrics.update(parsed_metrics)
|
|
@@ -4,17 +4,20 @@ Forgexa CLI — command-line client for the Forgexa platform.
|
|
|
4
4
|
A lightweight, standalone CLI that communicates with the Forgexa server via REST API.
|
|
5
5
|
Zero external dependencies — uses only Python stdlib.
|
|
6
6
|
|
|
7
|
-
Configuration:
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Configuration (in priority order):
|
|
8
|
+
1. --server-url flag Per-command override
|
|
9
|
+
2. FORGEXA_SERVER_URL env var Session-level override
|
|
10
|
+
3. ~/.forgexa/config Saved via `forgexa login --server <url>`
|
|
11
|
+
4. Build default https://api.forgexa.net
|
|
10
12
|
|
|
11
13
|
Usage:
|
|
12
|
-
forgexa login
|
|
14
|
+
forgexa login --server https://your-server.com
|
|
13
15
|
forgexa workspace list
|
|
14
16
|
forgexa project list --workspace <id>
|
|
15
17
|
forgexa requirement list --project <id>
|
|
16
18
|
forgexa daemon status
|
|
17
19
|
forgexa gates pending
|
|
20
|
+
forgexa config show
|
|
18
21
|
forgexa --help
|
|
19
22
|
"""
|
|
20
23
|
from __future__ import annotations
|
|
@@ -27,23 +30,56 @@ import signal
|
|
|
27
30
|
import sys
|
|
28
31
|
from pathlib import Path
|
|
29
32
|
|
|
30
|
-
# ──
|
|
33
|
+
# ── Build-time default ──
|
|
34
|
+
# Resolved in priority order at runtime (see _api_url()):
|
|
35
|
+
# 1. --server-url flag
|
|
36
|
+
# 2. FORGEXA_SERVER_URL environment variable
|
|
37
|
+
# 3. ~/.forgexa/config (saved by `forgexa login --server <url>`)
|
|
38
|
+
# 4. This build default
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
# Default server URL — resolved in priority order:
|
|
34
|
-
# 1. FORGEXA_SERVER_URL environment variable (runtime override)
|
|
35
|
-
# 2. _build_config.py — generated by publish.sh at wheel-build time
|
|
36
|
-
# 3. Hardcoded fallback — https://api.forgexa.net
|
|
37
|
-
#
|
|
38
|
-
# For local development use: FORGEXA_SERVER_URL=http://localhost:8000 forgexa ...
|
|
39
40
|
try:
|
|
40
|
-
from forgexa_cli._build_config import BUILD_SERVER_URL as
|
|
41
|
+
from forgexa_cli._build_config import BUILD_SERVER_URL as _BUILD_DEFAULT
|
|
41
42
|
except ImportError:
|
|
42
|
-
|
|
43
|
+
_BUILD_DEFAULT = "https://api.forgexa.net"
|
|
44
|
+
|
|
45
|
+
# Module-level override applied by main() when --server-url is passed.
|
|
46
|
+
_SERVER_URL_OVERRIDE: str | None = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ── Config file helpers (~/.forgexa/config) ──
|
|
50
|
+
|
|
51
|
+
def _config_path() -> Path:
|
|
52
|
+
return Path.home() / ".forgexa" / "config"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _load_config() -> dict:
|
|
56
|
+
p = _config_path()
|
|
57
|
+
if p.exists():
|
|
58
|
+
try:
|
|
59
|
+
return json.loads(p.read_text())
|
|
60
|
+
except Exception:
|
|
61
|
+
return {}
|
|
62
|
+
return {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _save_config(data: dict) -> None:
|
|
66
|
+
p = _config_path()
|
|
67
|
+
p.parent.mkdir(exist_ok=True)
|
|
68
|
+
p.write_text(json.dumps(data, indent=2))
|
|
69
|
+
p.chmod(0o600)
|
|
43
70
|
|
|
44
71
|
|
|
45
72
|
def _api_url() -> str:
|
|
46
|
-
|
|
73
|
+
"""Resolve the server URL using priority chain."""
|
|
74
|
+
if _SERVER_URL_OVERRIDE:
|
|
75
|
+
return _SERVER_URL_OVERRIDE
|
|
76
|
+
env = os.environ.get("FORGEXA_SERVER_URL")
|
|
77
|
+
if env:
|
|
78
|
+
return env
|
|
79
|
+
cfg = _load_config()
|
|
80
|
+
if cfg.get("server_url"):
|
|
81
|
+
return cfg["server_url"]
|
|
82
|
+
return _BUILD_DEFAULT
|
|
47
83
|
|
|
48
84
|
|
|
49
85
|
def _token() -> str | None:
|
|
@@ -53,7 +89,8 @@ def _token() -> str | None:
|
|
|
53
89
|
token_file = Path.home() / ".forgexa" / "token"
|
|
54
90
|
if token_file.exists():
|
|
55
91
|
return token_file.read_text().strip()
|
|
56
|
-
|
|
92
|
+
cfg = _load_config()
|
|
93
|
+
return cfg.get("token") or None
|
|
57
94
|
|
|
58
95
|
|
|
59
96
|
def _headers() -> dict[str, str]:
|
|
@@ -157,26 +194,83 @@ def _print_table(headers: list[str], rows: list[list[str]], fmt: str | None = No
|
|
|
157
194
|
|
|
158
195
|
|
|
159
196
|
def cmd_login(args: argparse.Namespace) -> None:
|
|
197
|
+
# If --server was given, apply it for this login request and save it.
|
|
198
|
+
server = getattr(args, "server", None)
|
|
199
|
+
if server:
|
|
200
|
+
global _SERVER_URL_OVERRIDE
|
|
201
|
+
_SERVER_URL_OVERRIDE = server.rstrip("/")
|
|
202
|
+
|
|
160
203
|
email = args.email or input("Email: ")
|
|
161
204
|
password = args.password or getpass.getpass("Password: ")
|
|
162
205
|
result = _post("/auth/login", {"email": email, "password": password})
|
|
163
206
|
token = result.get("access_token", "")
|
|
164
|
-
|
|
207
|
+
|
|
208
|
+
# Save to config file (server_url + token in one place)
|
|
209
|
+
cfg = _load_config()
|
|
210
|
+
if server:
|
|
211
|
+
cfg["server_url"] = _SERVER_URL_OVERRIDE
|
|
212
|
+
cfg["token"] = token
|
|
213
|
+
_save_config(cfg)
|
|
214
|
+
|
|
215
|
+
# Also keep the legacy token file for backwards compatibility
|
|
165
216
|
token_dir = Path.home() / ".forgexa"
|
|
166
217
|
token_dir.mkdir(exist_ok=True)
|
|
167
218
|
(token_dir / "token").write_text(token)
|
|
168
219
|
(token_dir / "token").chmod(0o600)
|
|
169
|
-
|
|
170
|
-
|
|
220
|
+
|
|
221
|
+
active_server = _api_url()
|
|
222
|
+
print(f"Login successful.")
|
|
223
|
+
print(f" Server : {active_server}")
|
|
224
|
+
print(f" Config : ~/.forgexa/config")
|
|
171
225
|
|
|
172
226
|
|
|
173
227
|
def cmd_logout(_args: argparse.Namespace) -> None:
|
|
228
|
+
cleared = False
|
|
174
229
|
token_file = Path.home() / ".forgexa" / "token"
|
|
175
230
|
if token_file.exists():
|
|
176
231
|
token_file.unlink()
|
|
232
|
+
cleared = True
|
|
233
|
+
cfg = _load_config()
|
|
234
|
+
if "token" in cfg:
|
|
235
|
+
del cfg["token"]
|
|
236
|
+
_save_config(cfg)
|
|
237
|
+
cleared = True
|
|
238
|
+
if cleared:
|
|
177
239
|
print("Logged out. Token removed.")
|
|
178
240
|
else:
|
|
179
|
-
print("No token
|
|
241
|
+
print("No token found.")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def cmd_config_show(_args: argparse.Namespace) -> None:
|
|
245
|
+
"""Show current CLI configuration."""
|
|
246
|
+
cfg = _load_config()
|
|
247
|
+
token = _token()
|
|
248
|
+
active_url = _api_url()
|
|
249
|
+
source = "build default"
|
|
250
|
+
if _SERVER_URL_OVERRIDE:
|
|
251
|
+
source = "--server-url flag"
|
|
252
|
+
elif os.environ.get("FORGEXA_SERVER_URL"):
|
|
253
|
+
source = "FORGEXA_SERVER_URL env var"
|
|
254
|
+
elif cfg.get("server_url"):
|
|
255
|
+
source = f"~/.forgexa/config"
|
|
256
|
+
print(f"Server URL : {active_url} (source: {source})")
|
|
257
|
+
print(f"Auth token : {'set' if token else 'not set'}")
|
|
258
|
+
print(f"Config file: {_config_path()}")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def cmd_config_set(args: argparse.Namespace) -> None:
|
|
262
|
+
"""Set a configuration value."""
|
|
263
|
+
cfg = _load_config()
|
|
264
|
+
if args.key == "server-url":
|
|
265
|
+
url = args.value.rstrip("/")
|
|
266
|
+
cfg["server_url"] = url
|
|
267
|
+
_save_config(cfg)
|
|
268
|
+
print(f"Server URL set to: {url}")
|
|
269
|
+
print(f"Saved to: {_config_path()}")
|
|
270
|
+
else:
|
|
271
|
+
print(f"Unknown config key: {args.key}", file=sys.stderr)
|
|
272
|
+
print("Available keys: server-url", file=sys.stderr)
|
|
273
|
+
sys.exit(1)
|
|
180
274
|
|
|
181
275
|
|
|
182
276
|
def cmd_daemon_status(_args: argparse.Namespace) -> None:
|
|
@@ -467,20 +561,28 @@ def cmd_version(_args: argparse.Namespace) -> None:
|
|
|
467
561
|
|
|
468
562
|
|
|
469
563
|
def main() -> None:
|
|
564
|
+
global _SERVER_URL_OVERRIDE, _output_format
|
|
565
|
+
|
|
566
|
+
active_url = _api_url() # resolved before parsing (for help text)
|
|
567
|
+
|
|
470
568
|
parser = argparse.ArgumentParser(
|
|
471
569
|
prog="forgexa",
|
|
472
570
|
description="Forgexa CLI — communicates with the Forgexa server via REST API.",
|
|
473
571
|
epilog=(
|
|
474
572
|
"Configuration:\n"
|
|
475
|
-
f"
|
|
476
|
-
"
|
|
573
|
+
f" Current server : {active_url}\n"
|
|
574
|
+
" Change server : forgexa login --server <url>\n"
|
|
575
|
+
" forgexa config set server-url <url>\n"
|
|
576
|
+
" export FORGEXA_SERVER_URL=<url>\n"
|
|
577
|
+
" Auth token : forgexa login (saved to ~/.forgexa/config)\n"
|
|
477
578
|
),
|
|
478
579
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
479
580
|
)
|
|
480
581
|
parser.add_argument(
|
|
481
582
|
"--server-url",
|
|
482
583
|
default=None,
|
|
483
|
-
|
|
584
|
+
metavar="URL",
|
|
585
|
+
help="Server URL override for this command (also: $FORGEXA_SERVER_URL or ~/.forgexa/config)",
|
|
484
586
|
)
|
|
485
587
|
parser.add_argument(
|
|
486
588
|
"--format",
|
|
@@ -495,10 +597,19 @@ def main() -> None:
|
|
|
495
597
|
|
|
496
598
|
# auth
|
|
497
599
|
login_p = sub.add_parser("login", help="Login and save access token")
|
|
600
|
+
login_p.add_argument("--server", metavar="URL", help="Server URL to connect to (saved to ~/.forgexa/config)")
|
|
498
601
|
login_p.add_argument("--email", help="Email address")
|
|
499
602
|
login_p.add_argument("--password", help="Password")
|
|
500
603
|
sub.add_parser("logout", help="Remove saved access token")
|
|
501
604
|
|
|
605
|
+
# config
|
|
606
|
+
config_p = sub.add_parser("config", help="View or change CLI configuration")
|
|
607
|
+
config_sub = config_p.add_subparsers(dest="config_cmd")
|
|
608
|
+
config_sub.add_parser("show", help="Show current configuration")
|
|
609
|
+
cfg_set = config_sub.add_parser("set", help="Set a configuration value")
|
|
610
|
+
cfg_set.add_argument("key", choices=["server-url"], help="Config key")
|
|
611
|
+
cfg_set.add_argument("value", help="Config value")
|
|
612
|
+
|
|
502
613
|
# daemon (remote API only — use forgexa-daemon for local daemon management)
|
|
503
614
|
daemon_p = sub.add_parser("daemon", help="Daemon management")
|
|
504
615
|
daemon_sub = daemon_p.add_subparsers(dest="daemon_cmd")
|
|
@@ -583,9 +694,8 @@ def main() -> None:
|
|
|
583
694
|
args = parser.parse_args()
|
|
584
695
|
|
|
585
696
|
if args.server_url:
|
|
586
|
-
|
|
697
|
+
_SERVER_URL_OVERRIDE = args.server_url.rstrip("/")
|
|
587
698
|
|
|
588
|
-
global _output_format
|
|
589
699
|
_output_format = args.format or "table"
|
|
590
700
|
|
|
591
701
|
cmd = args.command
|
|
@@ -597,6 +707,10 @@ def main() -> None:
|
|
|
597
707
|
"version": cmd_version,
|
|
598
708
|
"login": cmd_login,
|
|
599
709
|
"logout": cmd_logout,
|
|
710
|
+
"config": lambda a: {
|
|
711
|
+
"show": cmd_config_show,
|
|
712
|
+
"set": cmd_config_set,
|
|
713
|
+
}.get(a.config_cmd, lambda _: config_p.print_help())(a),
|
|
600
714
|
"daemon": lambda a: {
|
|
601
715
|
"start": cmd_daemon_start,
|
|
602
716
|
"status": cmd_daemon_status,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|