Skip to content

Commit a4ec5d1

Browse files
AbirAbbasclaude
andcommitted
feat(control-plane): af install support for Go agent nodes
PackageMetadata gains an explicit language field with go.mod detection fallback (additive to config v1; Python manifests unchanged). entrypoint.build compiles the node at install time via a resolved Go toolchain (pyinterp-style discovery with actionable missing/too-old errors); af run launches the built binary with identical port, healthcheck, secret, and env semantics. Out-of-tree replace directives are refused with vendoring guidance, with an AGENTFIELD_GO_REPLACE override for dev installs. Service-layer install/start paths route through the shared dispatcher so both code paths stay in lockstep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 26a479e commit a4ec5d1

8 files changed

Lines changed: 985 additions & 27 deletions

File tree

control-plane/internal/core/services/agent_service.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,15 @@ func (as *DefaultAgentService) buildProcessConfig(agentNode packages.InstalledPa
590590
}
591591

592592
// Launch via the manifest entrypoint (e.g. "python -m pr_af.app"). When the
593-
// program token is python/python3, substitute the resolved interpreter.
593+
// program token is python/python3, substitute the resolved interpreter. A Go
594+
// node launches its install-time-built binary; a package-relative binary path
595+
// is resolved against the install dir so exec finds it regardless of cwd.
594596
startArgs := metadata.StartCommand()
595597
command := startArgs[0]
596598
args := startArgs[1:]
597-
if command == "python" || command == "python3" {
599+
if metadata.IsGo() {
600+
command = packages.GoBinaryProgram(agentNode.Path, command)
601+
} else if command == "python" || command == "python3" {
598602
command = pythonPath
599603
}
600604

control-plane/internal/core/services/package_service.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,9 +545,11 @@ func (ps *DefaultPackageService) copyFile(src, dst string) error {
545545
return err
546546
}
547547

548-
// installDependencies installs package dependencies
548+
// installDependencies installs package dependencies for the node's language
549+
// (Go build or Python venv), delegating to the shared, language-aware installer
550+
// so this service path handles Go nodes identically to the CLI installer.
549551
func (ps *DefaultPackageService) installDependencies(packagePath string, metadata *packages.PackageMetadata) error {
550-
return packages.InstallPythonDependencies(packagePath, metadata.Dependencies.Python, metadata.Dependencies.System)
552+
return packages.InstallDependencies(packagePath, metadata)
551553
}
552554

553555
// hasRequirementsFile checks if requirements.txt exists

control-plane/internal/packages/config_version_fixtures_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ name: pr-af
127127
version: 0.2.0
128128
description: Opens draft PRs from a task description
129129
author: Agent-Field
130+
language: python
130131
entrypoint:
131132
start: python -m pr_af.app
132133
healthcheck: /health
@@ -167,6 +168,15 @@ user_environment:
167168
if md.Name != "pr-af" || md.Version != "0.2.0" {
168169
t.Errorf("basics: name=%q version=%q", md.Name, md.Version)
169170
}
171+
// `language` is an additive optional field (added without a
172+
// config_version bump): the current-version fixture asserts the
173+
// reader extracts it and that "python" is not treated as a Go node.
174+
if md.Language != "python" {
175+
t.Errorf("language = %q, want python", md.Language)
176+
}
177+
if md.IsGo() {
178+
t.Errorf("a python node must not be classified as Go")
179+
}
170180
if got := md.StartCommand(); len(got) == 0 || got[0] != "python" {
171181
t.Errorf("StartCommand() = %v", got)
172182
}

0 commit comments

Comments
 (0)