relay-flow 0.3.0-alpha → 0.3.2-alpha
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.
- package/README.md +5 -5
- package/cmd/relay-flow/main.go +2 -2
- package/internal/harness/opencode/opencode.go +7 -5
- package/internal/harness/opencode/opencode_test.go +1 -1
- package/internal/harness/opencode/repo_setup.go +1 -1
- package/internal/harness/opencode/validation_test.go +106 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,13 +24,13 @@ same `relay-flow-plugin` package with host-specific entrypoints.
|
|
|
24
24
|
**OpenCode**
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
-
opencode plugin relay-flow-plugin@0.3.
|
|
27
|
+
opencode plugin relay-flow-plugin@0.3.2-alpha
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
**Pi**
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
pi install npm:relay-flow-plugin@0.3.
|
|
33
|
+
pi install npm:relay-flow-plugin@0.3.2-alpha
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Pi loads the package's `pi.ts` extension from its manifest. Do not add
|
|
@@ -160,7 +160,7 @@ OpenCode plugin configuration uses both entrypoints. The server entrypoint is li
|
|
|
160
160
|
```json
|
|
161
161
|
{
|
|
162
162
|
"$schema": "https://opencode.ai/config.json",
|
|
163
|
-
"plugin": ["relay-flow-plugin@0.3.
|
|
163
|
+
"plugin": ["relay-flow-plugin@0.3.2-alpha"]
|
|
164
164
|
}
|
|
165
165
|
```
|
|
166
166
|
|
|
@@ -169,7 +169,7 @@ The native HITL approval entrypoint is listed in `.opencode/tui.json`:
|
|
|
169
169
|
```json
|
|
170
170
|
{
|
|
171
171
|
"$schema": "https://opencode.ai/tui.json",
|
|
172
|
-
"plugin": ["relay-flow-plugin@0.3.
|
|
172
|
+
"plugin": ["relay-flow-plugin@0.3.2-alpha"]
|
|
173
173
|
}
|
|
174
174
|
```
|
|
175
175
|
|
|
@@ -185,7 +185,7 @@ Pi plugin: install the same published package manually in Pi's global package
|
|
|
185
185
|
settings before starting a Pi harness session:
|
|
186
186
|
|
|
187
187
|
```sh
|
|
188
|
-
pi install npm:relay-flow-plugin@0.3.
|
|
188
|
+
pi install npm:relay-flow-plugin@0.3.2-alpha
|
|
189
189
|
```
|
|
190
190
|
|
|
191
191
|
Relay-flow does not install or configure the package automatically. Pi resolves
|
package/cmd/relay-flow/main.go
CHANGED
|
@@ -853,8 +853,8 @@ func cmdServe(p paths.Paths, args []string) int {
|
|
|
853
853
|
}
|
|
854
854
|
|
|
855
855
|
var (
|
|
856
|
-
backgroundServeStartupTimeout =
|
|
857
|
-
backgroundServePollInterval =
|
|
856
|
+
backgroundServeStartupTimeout = 5 * time.Minute
|
|
857
|
+
backgroundServePollInterval = 250 * time.Millisecond
|
|
858
858
|
)
|
|
859
859
|
|
|
860
860
|
func startBackgroundServe(p paths.Paths, recover, debug bool) error {
|
|
@@ -88,7 +88,7 @@ func init() {
|
|
|
88
88
|
type Harness struct {
|
|
89
89
|
templates Config
|
|
90
90
|
// listAgents is the test seam; nil → real `opencode agent list`.
|
|
91
|
-
listAgents func(ctx context.Context) ([]string, error)
|
|
91
|
+
listAgents func(ctx context.Context, repoPath string) ([]string, error)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// New returns the production Harness.
|
|
@@ -110,7 +110,7 @@ func (h *Harness) SetupRepo(_ context.Context, repoPath string) error {
|
|
|
110
110
|
// ValidateAgent reports whether name is a known OpenCode agent for the
|
|
111
111
|
// repo, per `opencode agent list` (agent names are the unindented first
|
|
112
112
|
// tokens).
|
|
113
|
-
func (h *Harness) ValidateAgent(ctx context.Context,
|
|
113
|
+
func (h *Harness) ValidateAgent(ctx context.Context, repoPath, agent string) error {
|
|
114
114
|
if agent == "" {
|
|
115
115
|
return fmt.Errorf("opencode: agent name is empty")
|
|
116
116
|
}
|
|
@@ -118,7 +118,7 @@ func (h *Harness) ValidateAgent(ctx context.Context, _ string, agent string) err
|
|
|
118
118
|
if list == nil {
|
|
119
119
|
list = listAgents
|
|
120
120
|
}
|
|
121
|
-
names, err := list(ctx)
|
|
121
|
+
names, err := list(ctx, repoPath)
|
|
122
122
|
if err != nil {
|
|
123
123
|
return err
|
|
124
124
|
}
|
|
@@ -236,8 +236,10 @@ func relayFlowHome() (string, error) {
|
|
|
236
236
|
|
|
237
237
|
// listAgents runs `opencode agent list` and returns the agent names
|
|
238
238
|
// (the unindented lines' first tokens).
|
|
239
|
-
func listAgents(ctx context.Context) ([]string, error) {
|
|
240
|
-
|
|
239
|
+
func listAgents(ctx context.Context, repoPath string) ([]string, error) {
|
|
240
|
+
cmd := exec.CommandContext(ctx, "opencode", "agent", "list")
|
|
241
|
+
cmd.Dir = repoPath
|
|
242
|
+
out, err := cmd.Output()
|
|
241
243
|
if err != nil {
|
|
242
244
|
return nil, fmt.Errorf("opencode agent list: %w", err)
|
|
243
245
|
}
|
|
@@ -14,7 +14,7 @@ import (
|
|
|
14
14
|
"github.com/rajpopat27/relay-flow/internal/workflow"
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
const configuredPlugin = "relay-flow-plugin@0.3.
|
|
17
|
+
const configuredPlugin = "relay-flow-plugin@0.3.2-alpha"
|
|
18
18
|
|
|
19
19
|
func TestBuildCommandArgv(t *testing.T) {
|
|
20
20
|
t.Setenv("RELAY_FLOW_HOME", "/var/lib/relay-flow-test")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package opencode
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"runtime"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestValidateAgentPassesRepositoryToListAgentsSeam(t *testing.T) {
|
|
13
|
+
repoPath := t.TempDir()
|
|
14
|
+
var gotRepoPath string
|
|
15
|
+
h := New()
|
|
16
|
+
h.listAgents = func(_ context.Context, path string) ([]string, error) {
|
|
17
|
+
gotRepoPath = path
|
|
18
|
+
return []string{"build"}, nil
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if err := h.ValidateAgent(context.Background(), repoPath, "build"); err != nil {
|
|
22
|
+
t.Fatalf("ValidateAgent: %v", err)
|
|
23
|
+
}
|
|
24
|
+
if gotRepoPath != repoPath {
|
|
25
|
+
t.Fatalf("listAgents repoPath = %q, want %q", gotRepoPath, repoPath)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func TestValidateAgentUsesRepositoryWorkingDirectory(t *testing.T) {
|
|
30
|
+
if runtime.GOOS == "windows" {
|
|
31
|
+
t.Skip("fake opencode uses a POSIX shell")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
repoA := t.TempDir()
|
|
35
|
+
repoB := t.TempDir()
|
|
36
|
+
fakeDir := t.TempDir()
|
|
37
|
+
fake := filepath.Join(fakeDir, "opencode")
|
|
38
|
+
script := `#!/bin/sh
|
|
39
|
+
set -eu
|
|
40
|
+
[ "$#" -eq 2 ] && [ "$1" = "agent" ] && [ "$2" = "list" ] || exit 2
|
|
41
|
+
actual=$(pwd)
|
|
42
|
+
case "$actual" in
|
|
43
|
+
"$OPENCODE_REPO_A")
|
|
44
|
+
printf 'repo-a-agent details\n'
|
|
45
|
+
;;
|
|
46
|
+
"$OPENCODE_REPO_B")
|
|
47
|
+
printf 'repo-b-agent details\n'
|
|
48
|
+
;;
|
|
49
|
+
*)
|
|
50
|
+
printf 'unexpected working directory: %s\n' "$actual" >&2
|
|
51
|
+
exit 3
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
printf ' ignored-indented\n'
|
|
55
|
+
printf '[ignored-json]\n'
|
|
56
|
+
printf '{"ignored":true}\n'
|
|
57
|
+
`
|
|
58
|
+
if err := os.WriteFile(fake, []byte(script), 0o700); err != nil {
|
|
59
|
+
t.Fatal(err)
|
|
60
|
+
}
|
|
61
|
+
t.Setenv("PATH", fakeDir)
|
|
62
|
+
t.Setenv("OPENCODE_REPO_A", repoA)
|
|
63
|
+
t.Setenv("OPENCODE_REPO_B", repoB)
|
|
64
|
+
|
|
65
|
+
h := New()
|
|
66
|
+
if err := h.ValidateAgent(context.Background(), repoA, "repo-a-agent"); err != nil {
|
|
67
|
+
t.Fatalf("agent from repository A rejected: %v", err)
|
|
68
|
+
}
|
|
69
|
+
if err := h.ValidateAgent(context.Background(), repoB, "repo-b-agent"); err != nil {
|
|
70
|
+
t.Fatalf("agent from repository B rejected: %v", err)
|
|
71
|
+
}
|
|
72
|
+
if err := h.ValidateAgent(context.Background(), repoA, "repo-b-agent"); err == nil {
|
|
73
|
+
t.Fatal("repository B agent accepted while validating repository A")
|
|
74
|
+
}
|
|
75
|
+
if err := h.ValidateAgent(context.Background(), repoB, "repo-a-agent"); err == nil {
|
|
76
|
+
t.Fatal("repository A agent accepted while validating repository B")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func TestValidateAgentPropagatesListCommandFailure(t *testing.T) {
|
|
81
|
+
if runtime.GOOS == "windows" {
|
|
82
|
+
t.Skip("fake opencode uses a POSIX shell")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fakeDir := t.TempDir()
|
|
86
|
+
fake := filepath.Join(fakeDir, "opencode")
|
|
87
|
+
script := `#!/bin/sh
|
|
88
|
+
set -eu
|
|
89
|
+
[ "$#" -eq 2 ] && [ "$1" = "agent" ] && [ "$2" = "list" ] || exit 2
|
|
90
|
+
printf 'agent list failed\n' >&2
|
|
91
|
+
exit 17
|
|
92
|
+
`
|
|
93
|
+
if err := os.WriteFile(fake, []byte(script), 0o700); err != nil {
|
|
94
|
+
t.Fatal(err)
|
|
95
|
+
}
|
|
96
|
+
t.Setenv("PATH", fakeDir)
|
|
97
|
+
|
|
98
|
+
h := New()
|
|
99
|
+
err := h.ValidateAgent(context.Background(), t.TempDir(), "build")
|
|
100
|
+
if err == nil {
|
|
101
|
+
t.Fatal("ValidateAgent succeeded after opencode agent list failed")
|
|
102
|
+
}
|
|
103
|
+
if !strings.Contains(err.Error(), "opencode agent list") {
|
|
104
|
+
t.Fatalf("error = %q, want opencode agent list context", err)
|
|
105
|
+
}
|
|
106
|
+
}
|