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.
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/PKG-INFO +1 -1
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli/daemon.py +56 -23
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/pyproject.toml +1 -1
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/README.md +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.10.2 → forgexa_cli-1.10.3}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.10.
|
|
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.
|
|
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=
|
|
1211
|
-
"-c", "user.email=
|
|
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=
|
|
4574
|
-
"-c", "user.email=
|
|
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,
|
|
5817
|
-
"
|
|
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=
|
|
6199
|
-
"-c", "user.email=
|
|
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=
|
|
6222
|
-
"-c", "user.email=
|
|
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=
|
|
6260
|
-
"-c", "user.email=
|
|
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=
|
|
6386
|
-
"-c", "user.email=
|
|
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(
|
|
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=
|
|
6542
|
-
"-c", "user.email=
|
|
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=
|
|
6591
|
-
"-c", "user.email=
|
|
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
|
)
|
|
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
|
|
File without changes
|