forgexa-cli 1.55.0__tar.gz → 1.55.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/PKG-INFO +1 -1
  2. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/agent_core.py +33 -2
  4. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/daemon.py +1 -1
  5. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/main.py +55 -0
  6. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/PKG-INFO +1 -1
  7. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/pyproject.toml +1 -1
  8. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/README.md +0 -0
  9. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/_build_config.py +0 -0
  10. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/_local_bind.py +0 -0
  11. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/autoupgrade.py +0 -0
  12. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/py.typed +0 -0
  13. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/runtime_evidence.py +0 -0
  14. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli/zcode_protocol.py +0 -0
  15. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  16. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  17. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/entry_points.txt +0 -0
  18. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/requires.txt +0 -0
  19. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/forgexa_cli.egg-info/top_level.txt +0 -0
  20. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/setup.cfg +0 -0
  21. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_auth_and_runtime_commands.py +0 -0
  22. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_autoupgrade.py +0 -0
  23. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_check_command.py +0 -0
  24. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_expiry_warnings_and_revoke.py +0 -0
  25. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_local_bind_commands.py +0 -0
  26. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_runtime_credentials.py +0 -0
  27. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_session_credentials.py +0 -0
  28. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_silent_install.py +0 -0
  29. {forgexa_cli-1.55.0 → forgexa_cli-1.55.1}/tests/test_upgrade_observability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.55.0
3
+ Version: 1.55.1
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.55.0"
2
+ __version__ = "1.55.1"
@@ -2311,15 +2311,46 @@ def zcode_binary_fingerprint(binary: str | Path) -> str:
2311
2311
  return digest.hexdigest()
2312
2312
 
2313
2313
 
2314
- def zcode_build_verification_issue(binary: str | Path, env: dict[str, str] | None = None) -> str | None:
2315
- """Return a fail-closed reason unless the binary fingerprint is allowlisted."""
2314
+ ZCODE_ALLOWED_SHA256_FILE = Path.home() / ".forgexa" / "zcode-allowed-sha256"
2315
+
2316
+
2317
+ def zcode_allowed_fingerprints(env: dict[str, str] | None = None) -> set[str]:
2318
+ """Union of the env allowlist and the persistent approval file.
2319
+
2320
+ GUI-launched daemons (macOS Dock/Finder) never inherit shell exports, so
2321
+ an explicit `forgexa zcode approve-build` writes the approval here where
2322
+ every runtime can read it regardless of how it was launched.
2323
+ """
2316
2324
  source_env = os.environ if env is None else env
2317
2325
  allowed = {
2318
2326
  value.strip().lower()
2319
2327
  for value in source_env.get("FORGEXA_ZCODE_ALLOWED_SHA256", "").split(",")
2320
2328
  if value.strip()
2321
2329
  }
2330
+ try:
2331
+ lines = ZCODE_ALLOWED_SHA256_FILE.read_text(encoding="utf-8").splitlines()
2332
+ except OSError:
2333
+ lines = []
2334
+ for line in lines:
2335
+ value = line.split("#", 1)[0].strip()
2336
+ if value:
2337
+ allowed.add(value.lower())
2338
+ return allowed
2339
+
2340
+
2341
+ def zcode_build_verification_issue(binary: str | Path, env: dict[str, str] | None = None) -> str | None:
2342
+ """Return a fail-closed reason unless the binary fingerprint is allowlisted."""
2343
+ allowed = zcode_allowed_fingerprints(env)
2322
2344
  if not allowed:
2345
+ try:
2346
+ actual = zcode_binary_fingerprint(binary)
2347
+ except OSError:
2348
+ actual = ""
2349
+ if actual:
2350
+ return (
2351
+ "ZCode build is not approved; run `forgexa zcode approve-build` or set "
2352
+ f"FORGEXA_ZCODE_ALLOWED_SHA256={actual}"
2353
+ )
2323
2354
  return "ZCode build is not approved; configure FORGEXA_ZCODE_ALLOWED_SHA256"
2324
2355
  try:
2325
2356
  actual = zcode_binary_fingerprint(binary)
@@ -1150,7 +1150,7 @@ except (ImportError, ModuleNotFoundError):
1150
1150
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
1151
1151
  # Kept in sync with pyproject.toml version via bump-version.sh.
1152
1152
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
1153
- DAEMON_VERSION = "1.55.0"
1153
+ DAEMON_VERSION = "1.55.1"
1154
1154
 
1155
1155
 
1156
1156
  def _detect_client_type() -> str:
@@ -3573,6 +3573,45 @@ def cmd_upgrade(args: argparse.Namespace) -> None:
3573
3573
  # ── Argument parser ──
3574
3574
 
3575
3575
 
3576
+ def cmd_zcode_approve_build(args: argparse.Namespace) -> None:
3577
+ from . import agent_core
3578
+
3579
+ binary = os.environ.get("FACTORY_ZCODE_PATH") or args.binary or shutil.which("zcode")
3580
+ if not binary:
3581
+ print("error: zcode binary not found; install it or pass --binary PATH", file=sys.stderr)
3582
+ return
3583
+ try:
3584
+ fingerprint = agent_core.zcode_binary_fingerprint(binary)
3585
+ except OSError as exc:
3586
+ print(f"error: could not fingerprint {binary}: {exc}", file=sys.stderr)
3587
+ return
3588
+ path = agent_core.ZCODE_ALLOWED_SHA256_FILE
3589
+ existing = ""
3590
+ try:
3591
+ existing = path.read_text(encoding="utf-8")
3592
+ except OSError:
3593
+ pass
3594
+ approved = {
3595
+ line.split("#", 1)[0].strip().lower()
3596
+ for line in existing.splitlines() if line.split("#", 1)[0].strip()
3597
+ }
3598
+ if fingerprint.lower() in approved:
3599
+ print(f"already approved: {fingerprint}")
3600
+ print(f"allowlist file: {path}")
3601
+ return
3602
+ path.parent.mkdir(parents=True, exist_ok=True)
3603
+ suffix = "" if (not existing or existing.endswith("\n")) else "\n"
3604
+ path.write_text(
3605
+ existing + suffix
3606
+ + f"{fingerprint} # {binary} approved by `forgexa zcode approve-build`\n",
3607
+ encoding="utf-8",
3608
+ )
3609
+ print(f"approved ZCode build: {fingerprint}")
3610
+ print(f"binary: {binary}")
3611
+ print(f"written to: {path}")
3612
+ print("running daemons and desktop sidecars pick this up on their next probe (no restart needed)")
3613
+
3614
+
3576
3615
  def main() -> None:
3577
3616
  global _SERVER_URL_OVERRIDE, _output_format
3578
3617
 
@@ -3789,6 +3828,19 @@ def main() -> None:
3789
3828
  help="Per-agent timeout for the live execution test (default: 90)",
3790
3829
  )
3791
3830
 
3831
+ # zcode
3832
+ zcode_p = sub.add_parser("zcode", help="ZCode integration helpers")
3833
+ zcode_sub = zcode_p.add_subparsers(dest="zcode_cmd")
3834
+ zcode_approve_p = zcode_sub.add_parser(
3835
+ "approve-build",
3836
+ help="Persistently approve the installed ZCode binary fingerprint "
3837
+ "(works for GUI-launched daemons that never see shell exports)",
3838
+ )
3839
+ zcode_approve_p.add_argument(
3840
+ "--binary", default=None, metavar="PATH",
3841
+ help="Path to the zcode binary (default: $FACTORY_ZCODE_PATH or `zcode` on PATH)",
3842
+ )
3843
+
3792
3844
  # runtimes
3793
3845
  rt_p = sub.add_parser("runtimes", help="Runtime management")
3794
3846
  rt_sub = rt_p.add_subparsers(dest="rt_cmd")
@@ -3918,6 +3970,9 @@ def main() -> None:
3918
3970
  "logs": cmd_daemon_logs,
3919
3971
  "agents": cmd_daemon_agents,
3920
3972
  "check": cmd_check,
3973
+ "zcode": lambda a: {
3974
+ "approve-build": cmd_zcode_approve_build,
3975
+ }.get(a.zcode_cmd, lambda _: zcode_p.print_help())(a),
3921
3976
  "runtimes": lambda a: {
3922
3977
  "list": cmd_runtimes_list,
3923
3978
  }.get(a.rt_cmd, lambda _: rt_p.print_help())(a),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.55.0
3
+ Version: 1.55.1
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.55.0"
3
+ version = "1.55.1"
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 = "MIT"
File without changes
File without changes