forgexa-cli 1.10.2__tar.gz → 1.10.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.10.2
3
+ Version: 1.10.3
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: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.10.2"
2
+ __version__ = "1.10.3"
@@ -396,7 +396,7 @@ except (ImportError, ModuleNotFoundError):
396
396
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
397
397
  # Kept in sync with pyproject.toml version via bump-version.sh.
398
398
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
399
- DAEMON_VERSION = "1.10.2"
399
+ DAEMON_VERSION = "1.10.3"
400
400
 
401
401
 
402
402
  def _detect_client_type() -> str:
@@ -648,6 +648,31 @@ class TaskResult:
648
648
  git: dict = field(default_factory=dict)
649
649
 
650
650
 
651
+ def _resolve_git_author(project: dict) -> tuple[str, str]:
652
+ """Resolve git commit author (name, email) using a fallback chain.
653
+
654
+ Priority:
655
+ 1. Task triggering user — populated by the API server from ``graph.triggered_by``
656
+ (the human who submitted/re-triggered the workflow).
657
+ 2. Runtime owner user — populated by the API server from ``runtime.owner``
658
+ (the user who registered this daemon).
659
+ 3. Hardcoded fallback — ``"Forgexa Agent" / "agent@forgexa.net"``
660
+ (backwards-compatible default when neither is available).
661
+
662
+ Using real user identities makes commits auditable: customers can trace
663
+ each automated commit back to the person who initiated the workflow.
664
+ """
665
+ name = (project.get("triggered_by_git_name") or "").strip()
666
+ email = (project.get("triggered_by_git_email") or "").strip()
667
+ if name and email:
668
+ return name, email
669
+ name = (project.get("runtime_owner_git_name") or "").strip()
670
+ email = (project.get("runtime_owner_git_email") or "").strip()
671
+ if name and email:
672
+ return name, email
673
+ return "Forgexa Agent", "agent@forgexa.net"
674
+
675
+
651
676
  # ── Type-aware analysis outputs (inline fallback for standalone daemons) ──
652
677
  # Mirrors type_workflow_profiles.py — used when import is unavailable (CLI/Desktop).
653
678
  _ANALYSIS_OUTPUTS_BY_TYPE: dict[str, list[str]] = {
@@ -1206,9 +1231,10 @@ class WorkspaceManager:
1206
1231
  readme = ws_path / "README.md"
1207
1232
  readme.write_text(f"# {project_key}\n\nInitialized by Forgexa.\n")
1208
1233
  await self._git("add", ".", cwd=ws_path)
1234
+ _git_name, _git_email = _resolve_git_author(project)
1209
1235
  await self._git(
1210
- "-c", "user.name=Forgexa Agent",
1211
- "-c", "user.email=agent@forgexa.net",
1236
+ "-c", f"user.name={_git_name}",
1237
+ "-c", f"user.email={_git_email}",
1212
1238
  "commit", "-m", "Initial commit",
1213
1239
  cwd=ws_path,
1214
1240
  )
@@ -4569,9 +4595,10 @@ class RuntimeDaemon:
4569
4595
  # Remove physical files
4570
4596
  shutil.rmtree(str(dir_to_wipe), ignore_errors=True)
4571
4597
  # Commit the wipe so the branch diff is clean
4598
+ _git_name, _git_email = _resolve_git_author(task.project)
4572
4599
  await self._git(
4573
- "-c", "user.name=Forgexa Agent",
4574
- "-c", "user.email=agent@forgexa.net",
4600
+ "-c", f"user.name={_git_name}",
4601
+ "-c", f"user.email={_git_email}",
4575
4602
  "commit", "-m",
4576
4603
  f"cleanup: wipe analysis docs in {output_dir_norm} before fresh re-analysis",
4577
4604
  cwd=workspace_path,
@@ -5808,13 +5835,15 @@ class RuntimeDaemon:
5808
5835
  except Exception as msg_err:
5809
5836
  logger.warning("Failed to build rich commit message: %s — using fallback", msg_err)
5810
5837
  commit_msg = f"{task.node_type}({task.requirement_key or task.task_id}): {display_title}"
5838
+ _git_name, _git_email = _resolve_git_author(task.project)
5811
5839
  proc = await asyncio.create_subprocess_exec(
5812
5840
  "git", "commit", "-m", commit_msg,
5813
5841
  cwd=str(workspace_path),
5814
5842
  stdout=asyncio.subprocess.PIPE,
5815
5843
  stderr=asyncio.subprocess.PIPE,
5816
- env={**os.environ, "GIT_AUTHOR_NAME": "Forgexa Agent", "GIT_AUTHOR_EMAIL": "agent@forgexa.net",
5817
- "GIT_COMMITTER_NAME": "Forgexa Agent", "GIT_COMMITTER_EMAIL": "agent@forgexa.net"},
5844
+ env={**os.environ,
5845
+ "GIT_AUTHOR_NAME": _git_name, "GIT_AUTHOR_EMAIL": _git_email,
5846
+ "GIT_COMMITTER_NAME": _git_name, "GIT_COMMITTER_EMAIL": _git_email},
5818
5847
  )
5819
5848
  await proc.communicate()
5820
5849
  has_new_commit = True
@@ -5854,7 +5883,7 @@ class RuntimeDaemon:
5854
5883
  return {"push_error": f"Would push to default branch {current_branch}"}
5855
5884
 
5856
5885
  # ── Push ──
5857
- push_error = await self._push_branch(workspace_path, project_key)
5886
+ push_error = await self._push_branch(workspace_path, project_key, task=task)
5858
5887
  if push_error:
5859
5888
  return {"push_error": push_error}
5860
5889
  return {}
@@ -6148,6 +6177,7 @@ class RuntimeDaemon:
6148
6177
  """
6149
6178
  git = self.workspace_manager._git
6150
6179
  target = f"origin/{default_branch}"
6180
+ _git_name, _git_email = _resolve_git_author(task.project)
6151
6181
 
6152
6182
  # Fetch latest remote state
6153
6183
  try:
@@ -6195,8 +6225,8 @@ class RuntimeDaemon:
6195
6225
  # ── Tier 1: rebase (safe — branch not yet on remote) ──
6196
6226
  try:
6197
6227
  await git(
6198
- "-c", "user.name=Forgexa Agent",
6199
- "-c", "user.email=agent@forgexa.net",
6228
+ "-c", f"user.name={_git_name}",
6229
+ "-c", f"user.email={_git_email}",
6200
6230
  "rebase", target,
6201
6231
  cwd=workspace_path, timeout=120,
6202
6232
  )
@@ -6218,8 +6248,8 @@ class RuntimeDaemon:
6218
6248
  # ── Tier 2: merge ──
6219
6249
  try:
6220
6250
  await git(
6221
- "-c", "user.name=Forgexa Agent",
6222
- "-c", "user.email=agent@forgexa.net",
6251
+ "-c", f"user.name={_git_name}",
6252
+ "-c", f"user.email={_git_email}",
6223
6253
  "merge", target, "--no-edit",
6224
6254
  cwd=workspace_path, timeout=120,
6225
6255
  )
@@ -6242,8 +6272,7 @@ class RuntimeDaemon:
6242
6272
  in a clean state and push whatever we had before.
6243
6273
  """
6244
6274
  git = self.workspace_manager._git
6245
-
6246
- # 1. List conflicted files
6275
+ _git_name, _git_email = _resolve_git_author(task.project)
6247
6276
  try:
6248
6277
  diff_out = await git(
6249
6278
  "diff", "--name-only", "--diff-filter=U", cwd=workspace_path,
@@ -6256,8 +6285,8 @@ class RuntimeDaemon:
6256
6285
  # No actual conflicts (merge completed or something unusual)
6257
6286
  try:
6258
6287
  await git(
6259
- "-c", "user.name=Forgexa Agent",
6260
- "-c", "user.email=agent@forgexa.net",
6288
+ "-c", f"user.name={_git_name}",
6289
+ "-c", f"user.email={_git_email}",
6261
6290
  "merge", "--continue", cwd=workspace_path,
6262
6291
  )
6263
6292
  except RuntimeError:
@@ -6382,8 +6411,8 @@ class RuntimeDaemon:
6382
6411
  await git("add", "-A", cwd=workspace_path)
6383
6412
  try:
6384
6413
  await git(
6385
- "-c", "user.name=Forgexa Agent",
6386
- "-c", "user.email=agent@forgexa.net",
6414
+ "-c", f"user.name={_git_name}",
6415
+ "-c", f"user.email={_git_email}",
6387
6416
  "commit", "--no-edit",
6388
6417
  cwd=workspace_path,
6389
6418
  )
@@ -6399,9 +6428,13 @@ class RuntimeDaemon:
6399
6428
  except RuntimeError:
6400
6429
  await git("reset", "--hard", "HEAD", cwd=workspace_path)
6401
6430
 
6402
- async def _push_branch(self, workspace_path: Path, project_key: str = "default") -> str | None:
6431
+ async def _push_branch(
6432
+ self, workspace_path: Path, project_key: str = "default",
6433
+ task: "TaskInfo | None" = None,
6434
+ ) -> str | None:
6403
6435
  """Push the current branch to origin. Returns error message on failure, None on success."""
6404
6436
  git = self.workspace_manager._git
6437
+ _git_name, _git_email = _resolve_git_author(task.project if task else {})
6405
6438
  try:
6406
6439
  # Get current branch name
6407
6440
  branch = (await git("rev-parse", "--abbrev-ref", "HEAD", cwd=workspace_path)).strip()
@@ -6538,8 +6571,8 @@ class RuntimeDaemon:
6538
6571
  cwd=workspace_path,
6539
6572
  )
6540
6573
  await git(
6541
- "-c", "user.name=Forgexa Agent",
6542
- "-c", "user.email=agent@forgexa.net",
6574
+ "-c", f"user.name={_git_name}",
6575
+ "-c", f"user.email={_git_email}",
6543
6576
  "cherry-pick", *new_shas,
6544
6577
  cwd=workspace_path,
6545
6578
  )
@@ -6587,8 +6620,8 @@ class RuntimeDaemon:
6587
6620
  # version. This is correct: on an agent-managed feature
6588
6621
  # branch the latest agent output is always authoritative.
6589
6622
  await git(
6590
- "-c", "user.name=Forgexa Agent",
6591
- "-c", "user.email=agent@forgexa.net",
6623
+ "-c", f"user.name={_git_name}",
6624
+ "-c", f"user.email={_git_email}",
6592
6625
  "rebase", "-X", "theirs", f"origin/{branch}",
6593
6626
  cwd=workspace_path,
6594
6627
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.10.2
3
+ Version: 1.10.3
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: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.10.2"
3
+ version = "1.10.3"
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" }
File without changes
File without changes