relay-flow 0.2.2-alpha → 0.2.4-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 +154 -13
- package/cmd/relay-flow/commands_test.go +5 -1
- package/cmd/relay-flow/main.go +27 -1
- package/cmd/relay-flow/pi_wiring_test.go +64 -0
- package/cmd/relay-flow/serve.go +14 -0
- package/examples/beads-workflow.yaml +3 -3
- package/examples/config-reference.yaml +144 -0
- package/examples/minimal-beads-task-workflow.yaml +34 -0
- package/examples/minimal-jira-task-workflow.yaml +68 -0
- package/examples/workflow-reference.yaml +111 -0
- package/internal/execution/goworkflows/activities.go +19 -0
- package/internal/execution/goworkflows/engine.go +20 -0
- package/internal/execution/goworkflows/engine_test.go +1 -1
- package/internal/execution/goworkflows/fakes_test.go +34 -6
- package/internal/execution/goworkflows/interpreter.go +48 -1
- package/internal/execution/goworkflows/projection.go +52 -11
- package/internal/execution/goworkflows/recovery_test.go +129 -0
- package/internal/harness/opencode/opencode.go +4 -4
- package/internal/harness/opencode/opencode_test.go +59 -4
- package/internal/harness/opencode/repo_setup.go +42 -2
- package/internal/harness/pi/config_test.go +46 -0
- package/internal/harness/pi/lifecycle_test.go +69 -0
- package/internal/harness/pi/pi.go +261 -0
- package/internal/harness/pi/pi_test.go +322 -0
- package/internal/harness/pi/prompt_test.go +121 -0
- package/internal/harness/pi/testdata/pi-0.84.1/capture.json +126 -0
- package/internal/harness/pi/testdata/pi-0.84.1/noninteractive-output.txt +11 -0
- package/internal/harness/pi/testdata/pi-0.84.1/tui-output-sanitized.txt +17 -0
- package/internal/harness/pi/validation_test.go +168 -0
- package/internal/identity/identity.go +28 -1
- package/internal/identity/identity_test.go +40 -0
- package/internal/run/manager.go +193 -25
- package/internal/run/run.go +24 -6
- package/internal/run/run_manager_test.go +100 -0
- package/internal/runner/herdr/baseref.go +60 -0
- package/internal/runner/herdr/herdr.go +578 -0
- package/internal/runner/herdr/herdr_test.go +688 -0
- package/internal/runner/herdr/herdrcli/contract.go +128 -0
- package/internal/runner/herdr/herdrcli/exec.go +68 -0
- package/internal/runner/herdr/herdrcli/herdrcli_test.go +274 -0
- package/internal/runner/herdr/herdrcli/live_test.go +132 -0
- package/internal/runner/herdr/herdrcli/operations.go +281 -0
- package/internal/runner/herdr/herdrcli/response.go +116 -0
- package/internal/runner/herdr/herdrcli/testdata/empty-panes.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/empty-tabs.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-not-git-worktree.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-pane-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-workspace-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/error-worktree-not-found.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/malformed.json +1 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-close.json +6 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-get.json +25 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-list.json +45 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-process-info-shell.json +22 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-process-info.json +23 -0
- package/internal/runner/herdr/herdrcli/testdata/pane-rename.json +23 -0
- package/internal/runner/herdr/herdrcli/testdata/snapshot.json +213 -0
- package/internal/runner/herdr/herdrcli/testdata/strict-herdr.sh +175 -0
- package/internal/runner/herdr/herdrcli/testdata/tab-create.json +31 -0
- package/internal/runner/herdr/herdrcli/testdata/tab-list.json +26 -0
- package/internal/runner/herdr/herdrcli/testdata/workspace-close.json +6 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-create.json +58 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-list.json +35 -0
- package/internal/runner/herdr/herdrcli/testdata/worktree-open.json +59 -0
- package/internal/server/api_test.go +54 -0
- package/internal/server/client.go +11 -0
- package/internal/server/fixture_test.go +18 -0
- package/internal/server/server.go +16 -0
- package/internal/task/beads/beads.go +16 -16
- package/internal/task/beads/config_compatibility_test.go +7 -8
- package/internal/task/beads/status_compatibility_test.go +43 -0
- package/internal/task/jira/filters_test.go +32 -6
- package/internal/task/jira/helpers_test.go +3 -0
- package/internal/task/jira/jira.go +43 -17
- package/internal/task/jira/transition_defaults_test.go +43 -0
- package/internal/task/task.go +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:api:snapshot",
|
|
3
|
+
"result": {
|
|
4
|
+
"snapshot": {
|
|
5
|
+
"agents": [],
|
|
6
|
+
"focused_pane_id": "w1:p1",
|
|
7
|
+
"focused_tab_id": "w1:t1",
|
|
8
|
+
"focused_workspace_id": "w1",
|
|
9
|
+
"layouts": [
|
|
10
|
+
{
|
|
11
|
+
"area": {
|
|
12
|
+
"height": 39,
|
|
13
|
+
"width": 94,
|
|
14
|
+
"x": 26,
|
|
15
|
+
"y": 1
|
|
16
|
+
},
|
|
17
|
+
"focused_pane_id": "w1:p1",
|
|
18
|
+
"panes": [
|
|
19
|
+
{
|
|
20
|
+
"focused": true,
|
|
21
|
+
"pane_id": "w1:p1",
|
|
22
|
+
"rect": {
|
|
23
|
+
"height": 39,
|
|
24
|
+
"width": 94,
|
|
25
|
+
"x": 26,
|
|
26
|
+
"y": 1
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"splits": [],
|
|
31
|
+
"tab_id": "w1:t1",
|
|
32
|
+
"workspace_id": "w1",
|
|
33
|
+
"zoomed": false
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"area": {
|
|
37
|
+
"height": 39,
|
|
38
|
+
"width": 94,
|
|
39
|
+
"x": 26,
|
|
40
|
+
"y": 1
|
|
41
|
+
},
|
|
42
|
+
"focused_pane_id": "w2:p1",
|
|
43
|
+
"panes": [
|
|
44
|
+
{
|
|
45
|
+
"focused": true,
|
|
46
|
+
"pane_id": "w2:p1",
|
|
47
|
+
"rect": {
|
|
48
|
+
"height": 39,
|
|
49
|
+
"width": 94,
|
|
50
|
+
"x": 26,
|
|
51
|
+
"y": 1
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"splits": [],
|
|
56
|
+
"tab_id": "w2:t1",
|
|
57
|
+
"workspace_id": "w2",
|
|
58
|
+
"zoomed": false
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"area": {
|
|
62
|
+
"height": 39,
|
|
63
|
+
"width": 94,
|
|
64
|
+
"x": 26,
|
|
65
|
+
"y": 1
|
|
66
|
+
},
|
|
67
|
+
"focused_pane_id": "w2:p2",
|
|
68
|
+
"panes": [
|
|
69
|
+
{
|
|
70
|
+
"focused": true,
|
|
71
|
+
"pane_id": "w2:p2",
|
|
72
|
+
"rect": {
|
|
73
|
+
"height": 39,
|
|
74
|
+
"width": 94,
|
|
75
|
+
"x": 26,
|
|
76
|
+
"y": 1
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"splits": [],
|
|
81
|
+
"tab_id": "w2:t2",
|
|
82
|
+
"workspace_id": "w2",
|
|
83
|
+
"zoomed": false
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"panes": [
|
|
87
|
+
{
|
|
88
|
+
"agent_status": "unknown",
|
|
89
|
+
"cwd": "/work/payments",
|
|
90
|
+
"focused": true,
|
|
91
|
+
"foreground_cwd": "/work/payments",
|
|
92
|
+
"pane_id": "w1:p1",
|
|
93
|
+
"revision": 1,
|
|
94
|
+
"scroll": {
|
|
95
|
+
"max_offset_from_bottom": 0,
|
|
96
|
+
"offset_from_bottom": 0,
|
|
97
|
+
"viewport_rows": 39
|
|
98
|
+
},
|
|
99
|
+
"tab_id": "w1:t1",
|
|
100
|
+
"terminal_id": "term_65a5cfcc8c6361",
|
|
101
|
+
"terminal_title": "relay@relay-host:/work/payments",
|
|
102
|
+
"terminal_title_stripped": "relay@relay-host:/work/payments",
|
|
103
|
+
"workspace_id": "w1"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"agent_status": "unknown",
|
|
107
|
+
"cwd": "/work/worktrees/payments/pay-101",
|
|
108
|
+
"focused": false,
|
|
109
|
+
"foreground_cwd": "/work/worktrees/payments/pay-101",
|
|
110
|
+
"pane_id": "w2:p1",
|
|
111
|
+
"revision": 1,
|
|
112
|
+
"scroll": {
|
|
113
|
+
"max_offset_from_bottom": 0,
|
|
114
|
+
"offset_from_bottom": 0,
|
|
115
|
+
"viewport_rows": 39
|
|
116
|
+
},
|
|
117
|
+
"tab_id": "w2:t1",
|
|
118
|
+
"terminal_id": "term_65a5cfcc8cd9e2",
|
|
119
|
+
"terminal_title": "relay@relay-host:~/.herdr/worktrees/repo/pay-101",
|
|
120
|
+
"terminal_title_stripped": "relay@relay-host:~/.herdr/worktrees/repo/pay-101",
|
|
121
|
+
"workspace_id": "w2"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"agent_status": "unknown",
|
|
125
|
+
"cwd": "/work/worktrees/payments/pay-101",
|
|
126
|
+
"focused": false,
|
|
127
|
+
"foreground_cwd": "/work/worktrees/payments/pay-101",
|
|
128
|
+
"label": "PAY-101:coding",
|
|
129
|
+
"pane_id": "w2:p2",
|
|
130
|
+
"revision": 2,
|
|
131
|
+
"scroll": {
|
|
132
|
+
"max_offset_from_bottom": 0,
|
|
133
|
+
"offset_from_bottom": 0,
|
|
134
|
+
"viewport_rows": 39
|
|
135
|
+
},
|
|
136
|
+
"tab_id": "w2:t2",
|
|
137
|
+
"terminal_id": "term_65a5cfcc913e03",
|
|
138
|
+
"terminal_title": "sleep 300",
|
|
139
|
+
"terminal_title_stripped": "sleep 300",
|
|
140
|
+
"workspace_id": "w2"
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"protocol": 20,
|
|
144
|
+
"tabs": [
|
|
145
|
+
{
|
|
146
|
+
"agent_status": "unknown",
|
|
147
|
+
"focused": true,
|
|
148
|
+
"label": "1",
|
|
149
|
+
"number": 1,
|
|
150
|
+
"pane_count": 1,
|
|
151
|
+
"tab_id": "w1:t1",
|
|
152
|
+
"workspace_id": "w1"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"agent_status": "unknown",
|
|
156
|
+
"focused": false,
|
|
157
|
+
"label": "1",
|
|
158
|
+
"number": 1,
|
|
159
|
+
"pane_count": 1,
|
|
160
|
+
"tab_id": "w2:t1",
|
|
161
|
+
"workspace_id": "w2"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"agent_status": "unknown",
|
|
165
|
+
"focused": false,
|
|
166
|
+
"label": "PAY-101:coding",
|
|
167
|
+
"number": 2,
|
|
168
|
+
"pane_count": 1,
|
|
169
|
+
"tab_id": "w2:t2",
|
|
170
|
+
"workspace_id": "w2"
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"version": "0.8.2",
|
|
174
|
+
"workspaces": [
|
|
175
|
+
{
|
|
176
|
+
"active_tab_id": "w1:t1",
|
|
177
|
+
"agent_status": "unknown",
|
|
178
|
+
"focused": true,
|
|
179
|
+
"label": "repo",
|
|
180
|
+
"number": 1,
|
|
181
|
+
"pane_count": 1,
|
|
182
|
+
"tab_count": 1,
|
|
183
|
+
"workspace_id": "w1",
|
|
184
|
+
"worktree": {
|
|
185
|
+
"checkout_path": "/work/payments",
|
|
186
|
+
"is_linked_worktree": false,
|
|
187
|
+
"repo_key": "/work/payments/.git",
|
|
188
|
+
"repo_name": "repo",
|
|
189
|
+
"repo_root": "/work/payments"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"active_tab_id": "w2:t1",
|
|
194
|
+
"agent_status": "unknown",
|
|
195
|
+
"focused": false,
|
|
196
|
+
"label": "PAY-101",
|
|
197
|
+
"number": 2,
|
|
198
|
+
"pane_count": 2,
|
|
199
|
+
"tab_count": 2,
|
|
200
|
+
"workspace_id": "w2",
|
|
201
|
+
"worktree": {
|
|
202
|
+
"checkout_path": "/work/worktrees/payments/pay-101",
|
|
203
|
+
"is_linked_worktree": true,
|
|
204
|
+
"repo_key": "/work/payments/.git",
|
|
205
|
+
"repo_name": "repo",
|
|
206
|
+
"repo_root": "/work/payments"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
"type": "session_snapshot"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Strict fake `herdr`. It accepts only the exact production command shapes and
|
|
3
|
+
# reproduces the observed transport contract of herdr 0.8.2:
|
|
4
|
+
# success -> result envelope on stdout, exit 0 (pane run prints nothing)
|
|
5
|
+
# failure -> error envelope on stderr, exit 1, empty stdout
|
|
6
|
+
set -eu
|
|
7
|
+
|
|
8
|
+
fail() {
|
|
9
|
+
printf 'unsupported or malformed herdr invocation:' >&2
|
|
10
|
+
for arg in "$@"; do
|
|
11
|
+
printf ' <%s>' "$arg" >&2
|
|
12
|
+
done
|
|
13
|
+
printf '\n' >&2
|
|
14
|
+
exit 64
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# The wrapper supplies selector configuration through the environment rather
|
|
18
|
+
# than adding session/socket flags to every Herdr command.
|
|
19
|
+
check_selector() {
|
|
20
|
+
actual_name=$1
|
|
21
|
+
expected_name=$2
|
|
22
|
+
eval "expected_set=\${${expected_name}+x}"
|
|
23
|
+
if [ "$expected_set" = x ]; then
|
|
24
|
+
eval "expected=\${${expected_name}-}"
|
|
25
|
+
eval "actual=\${${actual_name}-}"
|
|
26
|
+
[ "$actual" = "$expected" ] || fail "wrong $actual_name: $actual"
|
|
27
|
+
else
|
|
28
|
+
eval "actual_set=\${${actual_name}+x}"
|
|
29
|
+
[ "$actual_set" != x ] || fail "ambient $actual_name leaked"
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
check_selector HERDR_SESSION HERDR_EXPECT_SESSION
|
|
34
|
+
check_selector HERDR_SOCKET_PATH HERDR_EXPECT_SOCKET_PATH
|
|
35
|
+
|
|
36
|
+
fixture_path() {
|
|
37
|
+
fixture_dir=${HERDR_FIXTURE_DIR-}
|
|
38
|
+
[ -n "$fixture_dir" ] || fail "HERDR_FIXTURE_DIR is unset"
|
|
39
|
+
[ -f "$fixture_dir/$1" ] || fail "missing fixture: $1"
|
|
40
|
+
printf '%s' "$fixture_dir/$1"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
emit_result() { cat "$(fixture_path "$1")"; exit 0; }
|
|
44
|
+
|
|
45
|
+
# Herdr writes error envelopes to stderr and exits 1.
|
|
46
|
+
emit_error() { cat "$(fixture_path "$1")" >&2; exit 1; }
|
|
47
|
+
|
|
48
|
+
check_absolute_cwd() {
|
|
49
|
+
case "$1" in
|
|
50
|
+
/*) ;;
|
|
51
|
+
*) fail "--cwd must be absolute: $1" ;;
|
|
52
|
+
esac
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
check_value() {
|
|
56
|
+
[ -n "$1" ] || fail "empty positional value"
|
|
57
|
+
case "$1" in
|
|
58
|
+
-*) fail "unexpected flag in positional value: $1" ;;
|
|
59
|
+
esac
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "$#:${1-}:${2-}" in
|
|
63
|
+
2:api:snapshot)
|
|
64
|
+
emit_result snapshot.json
|
|
65
|
+
;;
|
|
66
|
+
4:worktree:list)
|
|
67
|
+
[ "$3" = --cwd ] || fail "$@"
|
|
68
|
+
check_absolute_cwd "$4"
|
|
69
|
+
case "$4" in
|
|
70
|
+
*not-a-repo) emit_error error-not-git-worktree.json ;;
|
|
71
|
+
*) emit_result worktree-list.json ;;
|
|
72
|
+
esac
|
|
73
|
+
;;
|
|
74
|
+
11:worktree:create)
|
|
75
|
+
[ "$3" = --cwd ] || fail "$@"
|
|
76
|
+
check_absolute_cwd "$4"
|
|
77
|
+
[ "$5" = --branch ] || fail "$@"
|
|
78
|
+
check_value "$6"
|
|
79
|
+
[ "$7" = --base ] || fail "$@"
|
|
80
|
+
check_value "$8"
|
|
81
|
+
[ "$9" = --label ] || fail "$@"
|
|
82
|
+
check_value "${10}"
|
|
83
|
+
[ "${11}" = --no-focus ] || fail "$@"
|
|
84
|
+
emit_result worktree-create.json
|
|
85
|
+
;;
|
|
86
|
+
9:worktree:open)
|
|
87
|
+
[ "$3" = --cwd ] || fail "$@"
|
|
88
|
+
check_absolute_cwd "$4"
|
|
89
|
+
[ "$5" = --branch ] || fail "$@"
|
|
90
|
+
check_value "$6"
|
|
91
|
+
[ "$7" = --label ] || fail "$@"
|
|
92
|
+
check_value "$8"
|
|
93
|
+
[ "$9" = --no-focus ] || fail "$@"
|
|
94
|
+
case "$6" in
|
|
95
|
+
MISSING-*) emit_error error-worktree-not-found.json ;;
|
|
96
|
+
esac
|
|
97
|
+
emit_result worktree-open.json
|
|
98
|
+
;;
|
|
99
|
+
9:tab:create)
|
|
100
|
+
[ "$3" = --workspace ] || fail "$@"
|
|
101
|
+
check_value "$4"
|
|
102
|
+
[ "$5" = --cwd ] || fail "$@"
|
|
103
|
+
check_absolute_cwd "$6"
|
|
104
|
+
[ "$7" = --label ] || fail "$@"
|
|
105
|
+
check_value "$8"
|
|
106
|
+
[ "$9" = --no-focus ] || fail "$@"
|
|
107
|
+
emit_result tab-create.json
|
|
108
|
+
;;
|
|
109
|
+
4:tab:list)
|
|
110
|
+
[ "$3" = --workspace ] || fail "$@"
|
|
111
|
+
check_value "$4"
|
|
112
|
+
case "$4" in
|
|
113
|
+
empty) emit_result empty-tabs.json ;;
|
|
114
|
+
missing) emit_error error-workspace-not-found.json ;;
|
|
115
|
+
*) emit_result tab-list.json ;;
|
|
116
|
+
esac
|
|
117
|
+
;;
|
|
118
|
+
4:pane:list)
|
|
119
|
+
[ "$3" = --workspace ] || fail "$@"
|
|
120
|
+
check_value "$4"
|
|
121
|
+
case "$4" in
|
|
122
|
+
empty) emit_result empty-panes.json ;;
|
|
123
|
+
missing) emit_error error-workspace-not-found.json ;;
|
|
124
|
+
*) emit_result pane-list.json ;;
|
|
125
|
+
esac
|
|
126
|
+
;;
|
|
127
|
+
3:pane:get)
|
|
128
|
+
check_value "$3"
|
|
129
|
+
case "$3" in
|
|
130
|
+
malformed) emit_result malformed.json ;;
|
|
131
|
+
warning) printf 'captured warning\n' >&2; emit_result pane-get.json ;;
|
|
132
|
+
missing) emit_error error-pane-not-found.json ;;
|
|
133
|
+
*) emit_result pane-get.json ;;
|
|
134
|
+
esac
|
|
135
|
+
;;
|
|
136
|
+
4:pane:process-info)
|
|
137
|
+
[ "$3" = --pane ] || fail "$@"
|
|
138
|
+
check_value "$4"
|
|
139
|
+
case "$4" in
|
|
140
|
+
shell) emit_result pane-process-info-shell.json ;;
|
|
141
|
+
missing) emit_error error-pane-not-found.json ;;
|
|
142
|
+
*) emit_result pane-process-info.json ;;
|
|
143
|
+
esac
|
|
144
|
+
;;
|
|
145
|
+
4:pane:rename)
|
|
146
|
+
check_value "$3"
|
|
147
|
+
check_value "$4"
|
|
148
|
+
emit_result pane-rename.json
|
|
149
|
+
;;
|
|
150
|
+
4:pane:run)
|
|
151
|
+
# COMMAND is deliberately one logical argument: a command containing
|
|
152
|
+
# spaces or newlines must arrive as the same argument the wrapper passed
|
|
153
|
+
# to exec.CommandContext. Herdr prints nothing on success.
|
|
154
|
+
check_value "$3"
|
|
155
|
+
[ -n "$4" ] || fail "empty command"
|
|
156
|
+
exit 0
|
|
157
|
+
;;
|
|
158
|
+
3:pane:close)
|
|
159
|
+
check_value "$3"
|
|
160
|
+
case "$3" in
|
|
161
|
+
missing) emit_error error-pane-not-found.json ;;
|
|
162
|
+
*) emit_result pane-close.json ;;
|
|
163
|
+
esac
|
|
164
|
+
;;
|
|
165
|
+
3:workspace:close)
|
|
166
|
+
check_value "$3"
|
|
167
|
+
case "$3" in
|
|
168
|
+
missing) emit_error error-workspace-not-found.json ;;
|
|
169
|
+
*) emit_result workspace-close.json ;;
|
|
170
|
+
esac
|
|
171
|
+
;;
|
|
172
|
+
*)
|
|
173
|
+
fail "$@"
|
|
174
|
+
;;
|
|
175
|
+
esac
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:tab:create",
|
|
3
|
+
"result": {
|
|
4
|
+
"root_pane": {
|
|
5
|
+
"agent_status": "unknown",
|
|
6
|
+
"cwd": "/work/worktrees/payments/pay-101",
|
|
7
|
+
"focused": false,
|
|
8
|
+
"foreground_cwd": "/work/worktrees/payments/pay-101",
|
|
9
|
+
"pane_id": "w2:p2",
|
|
10
|
+
"revision": 0,
|
|
11
|
+
"scroll": {
|
|
12
|
+
"max_offset_from_bottom": 0,
|
|
13
|
+
"offset_from_bottom": 0,
|
|
14
|
+
"viewport_rows": 39
|
|
15
|
+
},
|
|
16
|
+
"tab_id": "w2:t2",
|
|
17
|
+
"terminal_id": "term_65a5cfcc913e03",
|
|
18
|
+
"workspace_id": "w2"
|
|
19
|
+
},
|
|
20
|
+
"tab": {
|
|
21
|
+
"agent_status": "unknown",
|
|
22
|
+
"focused": false,
|
|
23
|
+
"label": "PAY-101:coding",
|
|
24
|
+
"number": 2,
|
|
25
|
+
"pane_count": 1,
|
|
26
|
+
"tab_id": "w2:t2",
|
|
27
|
+
"workspace_id": "w2"
|
|
28
|
+
},
|
|
29
|
+
"type": "tab_created"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:tab:list",
|
|
3
|
+
"result": {
|
|
4
|
+
"tabs": [
|
|
5
|
+
{
|
|
6
|
+
"agent_status": "unknown",
|
|
7
|
+
"focused": false,
|
|
8
|
+
"label": "1",
|
|
9
|
+
"number": 1,
|
|
10
|
+
"pane_count": 1,
|
|
11
|
+
"tab_id": "w2:t1",
|
|
12
|
+
"workspace_id": "w2"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"agent_status": "unknown",
|
|
16
|
+
"focused": false,
|
|
17
|
+
"label": "PAY-101:coding",
|
|
18
|
+
"number": 2,
|
|
19
|
+
"pane_count": 1,
|
|
20
|
+
"tab_id": "w2:t2",
|
|
21
|
+
"workspace_id": "w2"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"type": "tab_list"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:worktree:create",
|
|
3
|
+
"result": {
|
|
4
|
+
"root_pane": {
|
|
5
|
+
"agent_status": "unknown",
|
|
6
|
+
"cwd": "/work/worktrees/payments/pay-101",
|
|
7
|
+
"focused": false,
|
|
8
|
+
"foreground_cwd": "/work/worktrees/payments/pay-101",
|
|
9
|
+
"pane_id": "w2:p1",
|
|
10
|
+
"revision": 0,
|
|
11
|
+
"scroll": {
|
|
12
|
+
"max_offset_from_bottom": 0,
|
|
13
|
+
"offset_from_bottom": 0,
|
|
14
|
+
"viewport_rows": 40
|
|
15
|
+
},
|
|
16
|
+
"tab_id": "w2:t1",
|
|
17
|
+
"terminal_id": "term_65a5cfcc8cd9e2",
|
|
18
|
+
"workspace_id": "w2"
|
|
19
|
+
},
|
|
20
|
+
"tab": {
|
|
21
|
+
"agent_status": "unknown",
|
|
22
|
+
"focused": false,
|
|
23
|
+
"label": "1",
|
|
24
|
+
"number": 1,
|
|
25
|
+
"pane_count": 1,
|
|
26
|
+
"tab_id": "w2:t1",
|
|
27
|
+
"workspace_id": "w2"
|
|
28
|
+
},
|
|
29
|
+
"type": "worktree_created",
|
|
30
|
+
"workspace": {
|
|
31
|
+
"active_tab_id": "w2:t1",
|
|
32
|
+
"agent_status": "unknown",
|
|
33
|
+
"focused": false,
|
|
34
|
+
"label": "PAY-101",
|
|
35
|
+
"number": 2,
|
|
36
|
+
"pane_count": 1,
|
|
37
|
+
"tab_count": 1,
|
|
38
|
+
"workspace_id": "w2",
|
|
39
|
+
"worktree": {
|
|
40
|
+
"checkout_path": "/work/worktrees/payments/pay-101",
|
|
41
|
+
"is_linked_worktree": true,
|
|
42
|
+
"repo_key": "/work/payments/.git",
|
|
43
|
+
"repo_name": "repo",
|
|
44
|
+
"repo_root": "/work/payments"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"worktree": {
|
|
48
|
+
"branch": "PAY-101",
|
|
49
|
+
"is_bare": false,
|
|
50
|
+
"is_detached": false,
|
|
51
|
+
"is_linked_worktree": true,
|
|
52
|
+
"is_prunable": false,
|
|
53
|
+
"label": "repo",
|
|
54
|
+
"open_workspace_id": "w2",
|
|
55
|
+
"path": "/work/worktrees/payments/pay-101"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:worktree:list",
|
|
3
|
+
"result": {
|
|
4
|
+
"source": {
|
|
5
|
+
"repo_key": "/work/payments/.git",
|
|
6
|
+
"repo_name": "repo",
|
|
7
|
+
"repo_root": "/work/payments",
|
|
8
|
+
"source_checkout_path": "/work/payments",
|
|
9
|
+
"source_workspace_id": "w1"
|
|
10
|
+
},
|
|
11
|
+
"type": "worktree_list",
|
|
12
|
+
"worktrees": [
|
|
13
|
+
{
|
|
14
|
+
"branch": "main",
|
|
15
|
+
"is_bare": false,
|
|
16
|
+
"is_detached": false,
|
|
17
|
+
"is_linked_worktree": false,
|
|
18
|
+
"is_prunable": false,
|
|
19
|
+
"label": "repo",
|
|
20
|
+
"open_workspace_id": "w1",
|
|
21
|
+
"path": "/work/payments"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"branch": "PAY-101",
|
|
25
|
+
"is_bare": false,
|
|
26
|
+
"is_detached": false,
|
|
27
|
+
"is_linked_worktree": true,
|
|
28
|
+
"is_prunable": false,
|
|
29
|
+
"label": "repo",
|
|
30
|
+
"open_workspace_id": "w2",
|
|
31
|
+
"path": "/work/worktrees/payments/pay-101"
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cli:worktree:open",
|
|
3
|
+
"result": {
|
|
4
|
+
"already_open": true,
|
|
5
|
+
"root_pane": {
|
|
6
|
+
"agent_status": "unknown",
|
|
7
|
+
"cwd": "/work/worktrees/payments/pay-101",
|
|
8
|
+
"focused": false,
|
|
9
|
+
"foreground_cwd": "/work/worktrees/payments/pay-101",
|
|
10
|
+
"pane_id": "w2:p1",
|
|
11
|
+
"revision": 0,
|
|
12
|
+
"scroll": {
|
|
13
|
+
"max_offset_from_bottom": 0,
|
|
14
|
+
"offset_from_bottom": 0,
|
|
15
|
+
"viewport_rows": 39
|
|
16
|
+
},
|
|
17
|
+
"tab_id": "w2:t1",
|
|
18
|
+
"terminal_id": "term_65a5cfcc8cd9e2",
|
|
19
|
+
"workspace_id": "w2"
|
|
20
|
+
},
|
|
21
|
+
"tab": {
|
|
22
|
+
"agent_status": "unknown",
|
|
23
|
+
"focused": false,
|
|
24
|
+
"label": "1",
|
|
25
|
+
"number": 1,
|
|
26
|
+
"pane_count": 1,
|
|
27
|
+
"tab_id": "w2:t1",
|
|
28
|
+
"workspace_id": "w2"
|
|
29
|
+
},
|
|
30
|
+
"type": "worktree_opened",
|
|
31
|
+
"workspace": {
|
|
32
|
+
"active_tab_id": "w2:t1",
|
|
33
|
+
"agent_status": "unknown",
|
|
34
|
+
"focused": false,
|
|
35
|
+
"label": "PAY-101",
|
|
36
|
+
"number": 2,
|
|
37
|
+
"pane_count": 1,
|
|
38
|
+
"tab_count": 1,
|
|
39
|
+
"workspace_id": "w2",
|
|
40
|
+
"worktree": {
|
|
41
|
+
"checkout_path": "/work/worktrees/payments/pay-101",
|
|
42
|
+
"is_linked_worktree": true,
|
|
43
|
+
"repo_key": "/work/payments/.git",
|
|
44
|
+
"repo_name": "repo",
|
|
45
|
+
"repo_root": "/work/payments"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"worktree": {
|
|
49
|
+
"branch": "PAY-101",
|
|
50
|
+
"is_bare": false,
|
|
51
|
+
"is_detached": false,
|
|
52
|
+
"is_linked_worktree": true,
|
|
53
|
+
"is_prunable": false,
|
|
54
|
+
"label": "repo",
|
|
55
|
+
"open_workspace_id": "w2",
|
|
56
|
+
"path": "/work/worktrees/payments/pay-101"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -3,8 +3,10 @@ package server_test
|
|
|
3
3
|
import (
|
|
4
4
|
"bytes"
|
|
5
5
|
"encoding/json"
|
|
6
|
+
"fmt"
|
|
6
7
|
"io"
|
|
7
8
|
"net/http"
|
|
9
|
+
"strings"
|
|
8
10
|
"testing"
|
|
9
11
|
"time"
|
|
10
12
|
|
|
@@ -211,6 +213,58 @@ func TestRepoAndRunEndpoints(t *testing.T) {
|
|
|
211
213
|
}
|
|
212
214
|
}
|
|
213
215
|
|
|
216
|
+
func TestRunRestartEndpointReturnsFreshAttempt(t *testing.T) {
|
|
217
|
+
want := run.Run{
|
|
218
|
+
ID: "payments/basicFlow/PAY-101~attempt~2", LogicalID: "payments/basicFlow/PAY-101", AttemptID: 2,
|
|
219
|
+
Repo: "payments", Workflow: "basicFlow", Ticket: task.TicketRef{Key: "PAY-101"}, State: run.StateStarting,
|
|
220
|
+
}
|
|
221
|
+
fake := &fakeServices{restartRun: want}
|
|
222
|
+
c, cleanup := startHandler(t, fake)
|
|
223
|
+
defer cleanup()
|
|
224
|
+
|
|
225
|
+
code, env := do(t, c, http.MethodPost, "http://relay/runs/by-ticket/PAY-101/restart", nil)
|
|
226
|
+
if code != http.StatusOK || !env.OK {
|
|
227
|
+
t.Fatalf("POST restart: code=%d env=%+v", code, env)
|
|
228
|
+
}
|
|
229
|
+
if !bytes.Contains(env.Data, []byte(`"attemptId":2`)) ||
|
|
230
|
+
!bytes.Contains(env.Data, []byte(`"logicalRunId":"payments/basicFlow/PAY-101"`)) {
|
|
231
|
+
t.Fatalf("restart response omitted attempt identity: %s", env.Data)
|
|
232
|
+
}
|
|
233
|
+
if len(fake.restarts) != 1 || fake.restarts[0] != "PAY-101" {
|
|
234
|
+
t.Fatalf("restart calls = %v", fake.restarts)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
func TestRunRestartConflictMapsTo409(t *testing.T) {
|
|
239
|
+
fake := &fakeServices{restartErr: fmt.Errorf("%w: ticket is still canceling", run.ErrRestartConflict)}
|
|
240
|
+
c, cleanup := startHandler(t, fake)
|
|
241
|
+
defer cleanup()
|
|
242
|
+
code, env := do(t, c, http.MethodPost, "http://relay/runs/by-ticket/PAY-101/restart", nil)
|
|
243
|
+
if code != http.StatusConflict || env.OK || env.Error == nil || !strings.Contains(env.Error.Message, "ticket is still canceling") {
|
|
244
|
+
t.Fatalf("restart conflict: code=%d env=%+v", code, env)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
func TestBlockedRunGetShowsStatusAction(t *testing.T) {
|
|
249
|
+
blocked := run.Run{
|
|
250
|
+
ID: "payments/basicFlow/PAY-101~attempt~2", LogicalID: "payments/basicFlow/PAY-101", AttemptID: 2,
|
|
251
|
+
State: run.StateBlocked, CurrentNode: "start",
|
|
252
|
+
Ticket: task.TicketRef{Key: "PAY-101"},
|
|
253
|
+
LastError: "Human-owned ticket status \"Blocked\" conflicts with the workflow start transition. Move ticket PAY-101 to an allowed active start status; relay-flow will retry automatically",
|
|
254
|
+
}
|
|
255
|
+
c, cleanup := startHandler(t, &fakeServices{runs: []run.Run{blocked}})
|
|
256
|
+
defer cleanup()
|
|
257
|
+
code, env := do(t, c, http.MethodGet, "http://relay/runs/by-ticket/PAY-101", nil)
|
|
258
|
+
if code != http.StatusOK || !env.OK {
|
|
259
|
+
t.Fatalf("GET blocked run: code=%d env=%+v", code, env)
|
|
260
|
+
}
|
|
261
|
+
for _, want := range []string{`"state":"blocked"`, `"currentNode":"start"`, `allowed active start status`, `automatically`} {
|
|
262
|
+
if !bytes.Contains(env.Data, []byte(want)) {
|
|
263
|
+
t.Fatalf("blocked run response missing %q: %s", want, env.Data)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
214
268
|
func TestRunEndpointsExposeRetryDetails(t *testing.T) {
|
|
215
269
|
next := time.Now().UTC().Add(time.Minute)
|
|
216
270
|
retrying := run.Run{
|