forgexa-cli 1.0.4__tar.gz → 1.1.4__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.0.4 → forgexa_cli-1.1.4}/PKG-INFO +72 -2
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/README.md +70 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli/__init__.py +1 -1
- forgexa_cli-1.1.4/forgexa_cli/_build_config.py +2 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli/daemon.py +96 -4
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli/main.py +27 -9
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/PKG-INFO +72 -2
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/SOURCES.txt +1 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/pyproject.toml +2 -2
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.0.4 → forgexa_cli-1.1.4}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forgexa-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Jason Sun <dev.winds@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://forgexa.net
|
|
8
8
|
Project-URL: Documentation, https://docs.forgexa.net
|
|
@@ -228,3 +228,73 @@ password = pypi-AgEIcH...
|
|
|
228
228
|
EOF
|
|
229
229
|
chmod 600 ~/.pypirc
|
|
230
230
|
```
|
|
231
|
+
|
|
232
|
+
## Local Development & Debugging
|
|
233
|
+
|
|
234
|
+
### Editable Install
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
cd cli
|
|
238
|
+
pip install -e .
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This installs the `forgexa` command pointing to your local source. Changes take effect immediately.
|
|
242
|
+
|
|
243
|
+
### Testing Against Local Server
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Point CLI to local backend
|
|
247
|
+
export FORGEXA_SERVER_URL=http://localhost:8000
|
|
248
|
+
|
|
249
|
+
# Login
|
|
250
|
+
forgexa login
|
|
251
|
+
|
|
252
|
+
# Test commands
|
|
253
|
+
forgexa workspace list
|
|
254
|
+
forgexa daemon start
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Testing Against Remote/LAN Server
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
export FORGEXA_SERVER_URL=http://192.168.0.100:8000
|
|
261
|
+
forgexa login
|
|
262
|
+
forgexa daemon start
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Debugging the Daemon
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Run in foreground to see all logs
|
|
269
|
+
forgexa daemon start
|
|
270
|
+
|
|
271
|
+
# Check which agents are discovered
|
|
272
|
+
forgexa runtimes list
|
|
273
|
+
|
|
274
|
+
# Verbose logging (if supported)
|
|
275
|
+
DAEMON_LOG_LEVEL=DEBUG forgexa daemon start
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Project Structure
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
cli/
|
|
282
|
+
├── forgexa_cli/
|
|
283
|
+
│ ├── __init__.py # Version constant
|
|
284
|
+
│ ├── main.py # CLI entry point (argparse)
|
|
285
|
+
│ ├── daemon.py # Daemon implementation
|
|
286
|
+
│ └── py.typed # PEP 561 marker
|
|
287
|
+
├── scripts/
|
|
288
|
+
│ ├── bump-version.sh # Version management
|
|
289
|
+
│ ├── publish.sh # PyPI publishing
|
|
290
|
+
│ └── sync-daemon.sh # Sync daemon code from backend
|
|
291
|
+
├── pyproject.toml # Package metadata
|
|
292
|
+
└── README.md # This file
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Design Principles
|
|
296
|
+
|
|
297
|
+
- **Zero external dependencies** — uses only Python stdlib (urllib, json, subprocess)
|
|
298
|
+
- **Lightweight** — installs in seconds, no compilation needed
|
|
299
|
+
- **Cross-platform** — works on Linux, macOS, Windows
|
|
300
|
+
- **Standalone daemon** — discovers local AI agents without server-side configuration
|
|
@@ -199,3 +199,73 @@ password = pypi-AgEIcH...
|
|
|
199
199
|
EOF
|
|
200
200
|
chmod 600 ~/.pypirc
|
|
201
201
|
```
|
|
202
|
+
|
|
203
|
+
## Local Development & Debugging
|
|
204
|
+
|
|
205
|
+
### Editable Install
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
cd cli
|
|
209
|
+
pip install -e .
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This installs the `forgexa` command pointing to your local source. Changes take effect immediately.
|
|
213
|
+
|
|
214
|
+
### Testing Against Local Server
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Point CLI to local backend
|
|
218
|
+
export FORGEXA_SERVER_URL=http://localhost:8000
|
|
219
|
+
|
|
220
|
+
# Login
|
|
221
|
+
forgexa login
|
|
222
|
+
|
|
223
|
+
# Test commands
|
|
224
|
+
forgexa workspace list
|
|
225
|
+
forgexa daemon start
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Testing Against Remote/LAN Server
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
export FORGEXA_SERVER_URL=http://192.168.0.100:8000
|
|
232
|
+
forgexa login
|
|
233
|
+
forgexa daemon start
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Debugging the Daemon
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Run in foreground to see all logs
|
|
240
|
+
forgexa daemon start
|
|
241
|
+
|
|
242
|
+
# Check which agents are discovered
|
|
243
|
+
forgexa runtimes list
|
|
244
|
+
|
|
245
|
+
# Verbose logging (if supported)
|
|
246
|
+
DAEMON_LOG_LEVEL=DEBUG forgexa daemon start
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Project Structure
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
cli/
|
|
253
|
+
├── forgexa_cli/
|
|
254
|
+
│ ├── __init__.py # Version constant
|
|
255
|
+
│ ├── main.py # CLI entry point (argparse)
|
|
256
|
+
│ ├── daemon.py # Daemon implementation
|
|
257
|
+
│ └── py.typed # PEP 561 marker
|
|
258
|
+
├── scripts/
|
|
259
|
+
│ ├── bump-version.sh # Version management
|
|
260
|
+
│ ├── publish.sh # PyPI publishing
|
|
261
|
+
│ └── sync-daemon.sh # Sync daemon code from backend
|
|
262
|
+
├── pyproject.toml # Package metadata
|
|
263
|
+
└── README.md # This file
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Design Principles
|
|
267
|
+
|
|
268
|
+
- **Zero external dependencies** — uses only Python stdlib (urllib, json, subprocess)
|
|
269
|
+
- **Lightweight** — installs in seconds, no compilation needed
|
|
270
|
+
- **Cross-platform** — works on Linux, macOS, Windows
|
|
271
|
+
- **Standalone daemon** — discovers local AI agents without server-side configuration
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.1.4"
|
|
@@ -46,6 +46,10 @@ try:
|
|
|
46
46
|
except (ImportError, ModuleNotFoundError):
|
|
47
47
|
# Running standalone (e.g., from desktop app bundle or forgexa-cli).
|
|
48
48
|
# Provide a minimal settings object reading from environment variables.
|
|
49
|
+
# Production default for end-user installs; server-side deployments
|
|
50
|
+
# override via .env or systemd Environment= directives.
|
|
51
|
+
_STANDALONE_DEFAULT_URL = "https://api.forgexa.net"
|
|
52
|
+
|
|
49
53
|
class _StandaloneSettings:
|
|
50
54
|
"""Minimal settings shim for standalone daemon execution."""
|
|
51
55
|
|
|
@@ -55,7 +59,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
55
59
|
|
|
56
60
|
@property
|
|
57
61
|
def DAEMON_SERVER_URL(self) -> str:
|
|
58
|
-
return os.environ.get("DAEMON_SERVER_URL",
|
|
62
|
+
return os.environ.get("DAEMON_SERVER_URL", _STANDALONE_DEFAULT_URL)
|
|
59
63
|
|
|
60
64
|
@property
|
|
61
65
|
def DAEMON_SERVER_URLS(self) -> str:
|
|
@@ -105,11 +109,29 @@ except (ImportError, ModuleNotFoundError):
|
|
|
105
109
|
|
|
106
110
|
settings = _StandaloneSettings() # type: ignore[assignment]
|
|
107
111
|
|
|
112
|
+
# ── Logging — self-managed file handler ────────────────────────────────
|
|
113
|
+
# The daemon configures its own FileHandler so logs are written to
|
|
114
|
+
# ~/.forgexa/daemon/daemon.log regardless of how the daemon was launched
|
|
115
|
+
# (Makefile, Tauri desktop app, `forgexa daemon start --detach`, etc.).
|
|
116
|
+
# A StreamHandler is added only when stderr is a TTY (foreground mode)
|
|
117
|
+
# so logs are visible on the terminal without duplication.
|
|
118
|
+
_log_dir = Path.home() / ".forgexa" / "daemon"
|
|
119
|
+
_log_dir.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
DAEMON_LOG_PATH = _log_dir / "daemon.log"
|
|
121
|
+
|
|
122
|
+
_log_handlers: list[logging.Handler] = [
|
|
123
|
+
logging.FileHandler(DAEMON_LOG_PATH, mode="a", encoding="utf-8"),
|
|
124
|
+
]
|
|
125
|
+
if sys.stderr.isatty():
|
|
126
|
+
_log_handlers.append(logging.StreamHandler(sys.stderr))
|
|
127
|
+
|
|
108
128
|
logging.basicConfig(
|
|
109
129
|
level=logging.INFO,
|
|
110
130
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
131
|
+
handlers=_log_handlers,
|
|
111
132
|
)
|
|
112
133
|
logger = logging.getLogger("daemon")
|
|
134
|
+
logger.info("Daemon log: %s", DAEMON_LOG_PATH)
|
|
113
135
|
|
|
114
136
|
|
|
115
137
|
# ── Hardware ID — stable cross-IP machine fingerprint ──
|
|
@@ -313,21 +335,91 @@ class AgentDiscovery:
|
|
|
313
335
|
},
|
|
314
336
|
}
|
|
315
337
|
|
|
338
|
+
@staticmethod
|
|
339
|
+
def _expand_path():
|
|
340
|
+
"""Augment PATH with well-known agent CLI directories.
|
|
341
|
+
|
|
342
|
+
GUI apps (macOS, Windows) and systemd services often have a
|
|
343
|
+
minimal PATH that does not include user-installed CLI tools.
|
|
344
|
+
We probe common installation directories for each platform so
|
|
345
|
+
``shutil.which`` can locate agent binaries.
|
|
346
|
+
"""
|
|
347
|
+
home = Path.home()
|
|
348
|
+
is_win = sys.platform == "win32"
|
|
349
|
+
is_mac = sys.platform == "darwin"
|
|
350
|
+
|
|
351
|
+
extra_dirs: list[Path] = []
|
|
352
|
+
|
|
353
|
+
if is_win:
|
|
354
|
+
# Windows: npm global, AppData installs, scoop, cargo, bun
|
|
355
|
+
appdata = Path(os.environ.get("APPDATA", home / "AppData" / "Roaming"))
|
|
356
|
+
localappdata = Path(os.environ.get("LOCALAPPDATA", home / "AppData" / "Local"))
|
|
357
|
+
extra_dirs += [
|
|
358
|
+
appdata / "npm", # npm -g installs
|
|
359
|
+
localappdata / "Programs" / "Python" / "Scripts",
|
|
360
|
+
home / ".opencode" / "bin",
|
|
361
|
+
home / ".cargo" / "bin",
|
|
362
|
+
home / ".bun" / "bin",
|
|
363
|
+
home / "scoop" / "shims", # scoop package manager
|
|
364
|
+
]
|
|
365
|
+
# nvm-windows stores versions differently
|
|
366
|
+
nvm_home = os.environ.get("NVM_HOME", "")
|
|
367
|
+
if nvm_home:
|
|
368
|
+
nvm_path = Path(nvm_home)
|
|
369
|
+
nvm_symlink = os.environ.get("NVM_SYMLINK", "")
|
|
370
|
+
if nvm_symlink:
|
|
371
|
+
extra_dirs.append(Path(nvm_symlink))
|
|
372
|
+
elif nvm_path.is_dir():
|
|
373
|
+
versions = sorted(nvm_path.glob("v*/"), reverse=True)
|
|
374
|
+
if versions:
|
|
375
|
+
extra_dirs.append(versions[0])
|
|
376
|
+
else:
|
|
377
|
+
# macOS + Linux shared paths
|
|
378
|
+
extra_dirs += [
|
|
379
|
+
home / ".local" / "bin", # pip / pipx installs, Claude Code
|
|
380
|
+
home / ".opencode" / "bin", # opencode
|
|
381
|
+
home / ".cargo" / "bin", # Rust / cargo installs
|
|
382
|
+
home / ".bun" / "bin", # bun
|
|
383
|
+
home / ".volta" / "bin", # volta (node version manager)
|
|
384
|
+
]
|
|
385
|
+
if is_mac:
|
|
386
|
+
# Homebrew Intel + Apple Silicon
|
|
387
|
+
extra_dirs += [
|
|
388
|
+
Path("/usr/local/bin"),
|
|
389
|
+
Path("/opt/homebrew/bin"),
|
|
390
|
+
]
|
|
391
|
+
# nvm (macOS + Linux)
|
|
392
|
+
nvm_dir = os.environ.get("NVM_DIR", str(home / ".nvm"))
|
|
393
|
+
nvm_path = Path(nvm_dir)
|
|
394
|
+
if nvm_path.is_dir():
|
|
395
|
+
versions = sorted(nvm_path.glob("versions/node/*/bin"), reverse=True)
|
|
396
|
+
if versions:
|
|
397
|
+
extra_dirs.append(versions[0])
|
|
398
|
+
|
|
399
|
+
current = os.environ.get("PATH", "")
|
|
400
|
+
current_set = set(current.split(os.pathsep))
|
|
401
|
+
additions = [str(d) for d in extra_dirs if d.is_dir() and str(d) not in current_set]
|
|
402
|
+
if additions:
|
|
403
|
+
os.environ["PATH"] = os.pathsep.join(additions) + os.pathsep + current
|
|
404
|
+
logger.debug("Expanded PATH with: %s", ", ".join(additions))
|
|
405
|
+
|
|
316
406
|
async def discover(self) -> list[DiscoveredAgent]:
|
|
407
|
+
self._expand_path()
|
|
317
408
|
available = []
|
|
318
409
|
for agent_id, spec in self.AGENT_REGISTRY.items():
|
|
319
410
|
custom_path = os.environ.get(spec.get("env_path_override", ""))
|
|
320
411
|
cmd = custom_path or spec["commands"][0]
|
|
321
|
-
|
|
412
|
+
resolved = shutil.which(cmd)
|
|
413
|
+
if resolved:
|
|
322
414
|
version = await self._get_version(spec["detect"])
|
|
323
415
|
available.append(DiscoveredAgent(
|
|
324
416
|
agent_id=agent_id,
|
|
325
|
-
command=
|
|
417
|
+
command=resolved,
|
|
326
418
|
version=version,
|
|
327
419
|
invoke_modes=spec["invoke_modes"],
|
|
328
420
|
compatibility_level=spec["compatibility_level"],
|
|
329
421
|
))
|
|
330
|
-
logger.info("Discovered agent: %s v%s (%s)", agent_id, version,
|
|
422
|
+
logger.info("Discovered agent: %s v%s (%s)", agent_id, version, resolved)
|
|
331
423
|
return available
|
|
332
424
|
|
|
333
425
|
async def _get_version(self, detect_cmd: str) -> str:
|
|
@@ -5,7 +5,7 @@ A lightweight, standalone CLI that communicates with the Forgexa server via REST
|
|
|
5
5
|
Zero external dependencies — uses only Python stdlib.
|
|
6
6
|
|
|
7
7
|
Configuration:
|
|
8
|
-
FORGEXA_SERVER_URL Server URL (default:
|
|
8
|
+
FORGEXA_SERVER_URL Server URL (default: https://api.forgexa.net)
|
|
9
9
|
FORGEXA_TOKEN Bearer token (obtain via `forgexa login`)
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
@@ -30,8 +30,20 @@ from pathlib import Path
|
|
|
30
30
|
# ── HTTP helpers (stdlib only) ──
|
|
31
31
|
|
|
32
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
|
+
try:
|
|
40
|
+
from forgexa_cli._build_config import BUILD_SERVER_URL as _DEFAULT_SERVER_URL
|
|
41
|
+
except ImportError:
|
|
42
|
+
_DEFAULT_SERVER_URL = "https://api.forgexa.net"
|
|
43
|
+
|
|
44
|
+
|
|
33
45
|
def _api_url() -> str:
|
|
34
|
-
return os.environ.get("FORGEXA_SERVER_URL",
|
|
46
|
+
return os.environ.get("FORGEXA_SERVER_URL", _DEFAULT_SERVER_URL)
|
|
35
47
|
|
|
36
48
|
|
|
37
49
|
def _token() -> str | None:
|
|
@@ -218,20 +230,26 @@ def cmd_daemon_start(args: argparse.Namespace) -> None:
|
|
|
218
230
|
os.environ.setdefault("DAEMON_API_TOKEN", token)
|
|
219
231
|
|
|
220
232
|
if getattr(args, "detach", False):
|
|
221
|
-
# Background mode: spawn forgexa-daemon as detached subprocess
|
|
233
|
+
# Background mode: spawn forgexa-daemon as detached subprocess.
|
|
234
|
+
# Redirect stderr to the canonical log file so logs are not lost.
|
|
222
235
|
import subprocess as sp
|
|
223
236
|
|
|
237
|
+
log_path = Path.home() / ".forgexa" / "daemon" / "daemon.log"
|
|
238
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
239
|
+
|
|
224
240
|
cmd = [sys.executable, "-m", "forgexa_cli.daemon"]
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
241
|
+
with open(log_path, "a", encoding="utf-8") as log_fh:
|
|
242
|
+
proc = sp.Popen(
|
|
243
|
+
cmd,
|
|
244
|
+
stdout=sp.DEVNULL,
|
|
245
|
+
stderr=log_fh,
|
|
246
|
+
start_new_session=True,
|
|
247
|
+
)
|
|
231
248
|
pid_file = Path.home() / ".forgexa-daemon.pid"
|
|
232
249
|
pid_file.write_text(str(proc.pid))
|
|
233
250
|
print(f"Daemon started in background (PID {proc.pid})")
|
|
234
251
|
print(f"Server: {server_url}")
|
|
252
|
+
print(f"Logs: {log_path}")
|
|
235
253
|
print(f"Stop with: forgexa daemon stop")
|
|
236
254
|
else:
|
|
237
255
|
# Foreground mode: run daemon directly
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forgexa-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Jason Sun <dev.winds@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://forgexa.net
|
|
8
8
|
Project-URL: Documentation, https://docs.forgexa.net
|
|
@@ -228,3 +228,73 @@ password = pypi-AgEIcH...
|
|
|
228
228
|
EOF
|
|
229
229
|
chmod 600 ~/.pypirc
|
|
230
230
|
```
|
|
231
|
+
|
|
232
|
+
## Local Development & Debugging
|
|
233
|
+
|
|
234
|
+
### Editable Install
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
cd cli
|
|
238
|
+
pip install -e .
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This installs the `forgexa` command pointing to your local source. Changes take effect immediately.
|
|
242
|
+
|
|
243
|
+
### Testing Against Local Server
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Point CLI to local backend
|
|
247
|
+
export FORGEXA_SERVER_URL=http://localhost:8000
|
|
248
|
+
|
|
249
|
+
# Login
|
|
250
|
+
forgexa login
|
|
251
|
+
|
|
252
|
+
# Test commands
|
|
253
|
+
forgexa workspace list
|
|
254
|
+
forgexa daemon start
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Testing Against Remote/LAN Server
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
export FORGEXA_SERVER_URL=http://192.168.0.100:8000
|
|
261
|
+
forgexa login
|
|
262
|
+
forgexa daemon start
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Debugging the Daemon
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Run in foreground to see all logs
|
|
269
|
+
forgexa daemon start
|
|
270
|
+
|
|
271
|
+
# Check which agents are discovered
|
|
272
|
+
forgexa runtimes list
|
|
273
|
+
|
|
274
|
+
# Verbose logging (if supported)
|
|
275
|
+
DAEMON_LOG_LEVEL=DEBUG forgexa daemon start
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Project Structure
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
cli/
|
|
282
|
+
├── forgexa_cli/
|
|
283
|
+
│ ├── __init__.py # Version constant
|
|
284
|
+
│ ├── main.py # CLI entry point (argparse)
|
|
285
|
+
│ ├── daemon.py # Daemon implementation
|
|
286
|
+
│ └── py.typed # PEP 561 marker
|
|
287
|
+
├── scripts/
|
|
288
|
+
│ ├── bump-version.sh # Version management
|
|
289
|
+
│ ├── publish.sh # PyPI publishing
|
|
290
|
+
│ └── sync-daemon.sh # Sync daemon code from backend
|
|
291
|
+
├── pyproject.toml # Package metadata
|
|
292
|
+
└── README.md # This file
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Design Principles
|
|
296
|
+
|
|
297
|
+
- **Zero external dependencies** — uses only Python stdlib (urllib, json, subprocess)
|
|
298
|
+
- **Lightweight** — installs in seconds, no compilation needed
|
|
299
|
+
- **Cross-platform** — works on Linux, macOS, Windows
|
|
300
|
+
- **Standalone daemon** — discovers local AI agents without server-side configuration
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "forgexa-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.1.4"
|
|
4
4
|
description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
|
|
5
5
|
requires-python = ">=3.9"
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
authors = [
|
|
8
|
-
{ name = "
|
|
8
|
+
{ name = "Jason Sun", email = "dev.winds@gmail.com" },
|
|
9
9
|
]
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = ["forgexa", "ai", "software-factory", "cli", "devops", "agent"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|