Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf1c742e

Browse files
committed
Add cli command to report task status
1 parentc339066 commitf1c742e

File tree

5 files changed

+261
-0
lines changed

5 files changed

+261
-0
lines changed

‎cli/exp.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func (r *RootCmd) expCmd() *serpent.Command {
1616
r.mcpCommand(),
1717
r.promptExample(),
1818
r.rptyCommand(),
19+
r.taskCommand(),
1920
},
2021
}
2122
returncmd

‎cli/exp_task.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/url"
7+
"time"
8+
9+
agentapi"github.com/coder/agentapi-sdk-go"
10+
agentapigen"github.com/coder/agentapi-sdk-go/gen"
11+
"github.com/coder/coder/v2/cli/cliui"
12+
"github.com/coder/coder/v2/codersdk"
13+
"github.com/coder/coder/v2/codersdk/agentsdk"
14+
"github.com/coder/serpent"
15+
)
16+
17+
func (r*RootCmd)taskCommand()*serpent.Command {
18+
cmd:=&serpent.Command{
19+
Use:"task",
20+
Short:"Interact with AI tasks.",
21+
Handler:func(i*serpent.Invocation)error {
22+
returni.Command.HelpHandler(i)
23+
},
24+
Children: []*serpent.Command{
25+
r.taskReportStatus(),
26+
},
27+
}
28+
returncmd
29+
}
30+
31+
func (r*RootCmd)taskReportStatus()*serpent.Command {
32+
var (
33+
slugstring
34+
interval time.Duration
35+
llmURL url.URL
36+
)
37+
cmd:=&serpent.Command{
38+
Use:"report-status",
39+
Short:"Report status of the currently running task to Coder.",
40+
Handler:func(inv*serpent.Invocation)error {
41+
ctx:=inv.Context()
42+
43+
// This is meant to run in a workspace, so instead of a regular client we
44+
// need a workspace agent client to update the status in coderd.
45+
agentClient,err:=r.createAgentClient()
46+
iferr!=nil {
47+
returnerr
48+
}
49+
50+
// We also need an agentapi client to get the LLM agent's current status.
51+
llmClient,err:=agentapi.NewClient(llmURL.String())
52+
iferr!=nil {
53+
returnerr
54+
}
55+
56+
notifyCtx,notifyCancel:=inv.SignalNotifyContext(ctx,StopSignals...)
57+
defernotifyCancel()
58+
59+
outerLoop:
60+
for {
61+
res,err:=llmClient.GetStatus(notifyCtx)
62+
iferr!=nil&&!errors.Is(err,context.Canceled) {
63+
cliui.Warnf(inv.Stderr,"failed to fetch status: %s",err)
64+
}else {
65+
// Currently we only update the status, which leaves the last summary
66+
// (if any) untouched. If we do want to update the summary here, we
67+
// will need to fetch the messages and generate one.
68+
status:=codersdk.WorkspaceAppStatusStateWorking
69+
switchres.Status {
70+
caseagentapigen.Stable:// Stable == idle == done
71+
status=codersdk.WorkspaceAppStatusStateComplete
72+
caseagentapigen.Running:// Running == working
73+
}
74+
err=agentClient.PatchAppStatus(notifyCtx, agentsdk.PatchAppStatus{
75+
AppSlug:slug,
76+
State:status,
77+
})
78+
iferr!=nil&&!errors.Is(err,context.Canceled) {
79+
cliui.Warnf(inv.Stderr,"failed to update status: %s",err)
80+
}
81+
}
82+
83+
timer:=time.NewTimer(interval)
84+
select {
85+
case<-notifyCtx.Done():
86+
timer.Stop()
87+
break outerLoop
88+
case<-timer.C:
89+
}
90+
}
91+
92+
returnnil
93+
},
94+
Options: []serpent.Option{
95+
{
96+
Flag:"app-slug",
97+
Description:"The app slug to use when reporting the status.",
98+
Env:"CODER_MCP_APP_STATUS_SLUG",
99+
Required:true,
100+
Value:serpent.StringOf(&slug),
101+
},
102+
{
103+
Flag:"agentapi-url",
104+
Description:"The URL of the LLM agent API.",
105+
Env:"CODER_AGENTAPI_URL",
106+
Required:true,
107+
Value:serpent.URLOf(&llmURL),
108+
},
109+
{
110+
Flag:"interval",
111+
Description:"The interval on which to poll for the status.",
112+
Env:"CODER_APP_STATUS_INTERVAL",
113+
Default:"30s",
114+
Value:serpent.DurationOf(&interval),
115+
},
116+
},
117+
}
118+
returncmd
119+
}

‎cli/exp_task_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package cli_test
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
14+
agentapi"github.com/coder/agentapi-sdk-go"
15+
agentapigen"github.com/coder/agentapi-sdk-go/gen"
16+
"github.com/coder/coder/v2/cli/clitest"
17+
"github.com/coder/coder/v2/coderd/httpapi"
18+
"github.com/coder/coder/v2/codersdk"
19+
"github.com/coder/coder/v2/codersdk/agentsdk"
20+
"github.com/coder/coder/v2/pty/ptytest"
21+
"github.com/coder/coder/v2/testutil"
22+
)
23+
24+
funcTestExpTask(t*testing.T) {
25+
t.Parallel()
26+
27+
tests:= []struct {
28+
namestring
29+
resp*codersdk.Response
30+
status*agentapi.GetStatusResponse
31+
expected codersdk.WorkspaceAppStatusState
32+
}{
33+
{
34+
name:"ReportWorking",
35+
resp:nil,
36+
status:&agentapi.GetStatusResponse{
37+
Status:agentapigen.Running,
38+
},
39+
expected:codersdk.WorkspaceAppStatusStateWorking,
40+
},
41+
{
42+
name:"ReportComplete",
43+
resp:nil,
44+
status:&agentapi.GetStatusResponse{
45+
Status:agentapigen.Stable,
46+
},
47+
expected:codersdk.WorkspaceAppStatusStateComplete,
48+
},
49+
{
50+
name:"ReportUpdateError",
51+
resp:&codersdk.Response{
52+
Message:"Failed to get workspace app.",
53+
Detail:"This is a test failure.",
54+
},
55+
status:&agentapi.GetStatusResponse{
56+
Status:agentapigen.Stable,
57+
},
58+
expected:codersdk.WorkspaceAppStatusStateComplete,
59+
},
60+
{
61+
name:"ReportStatusError",
62+
resp:nil,
63+
status:nil,
64+
expected:codersdk.WorkspaceAppStatusStateComplete,
65+
},
66+
}
67+
68+
for_,test:=rangetests {
69+
test:=test
70+
t.Run(test.name,func(t*testing.T) {
71+
t.Parallel()
72+
done:=make(chan codersdk.WorkspaceAppStatusState)
73+
74+
// A mock server for coderd.
75+
srv:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
76+
body,err:=io.ReadAll(r.Body)
77+
require.NoError(t,err)
78+
_=r.Body.Close()
79+
80+
varreq agentsdk.PatchAppStatus
81+
err=json.Unmarshal(body,&req)
82+
require.NoError(t,err)
83+
84+
iftest.resp!=nil {
85+
httpapi.Write(context.Background(),w,http.StatusBadRequest,test.resp)
86+
}else {
87+
httpapi.Write(context.Background(),w,http.StatusOK,nil)
88+
}
89+
done<-req.State
90+
}))
91+
t.Cleanup(srv.Close)
92+
agentURL:=srv.URL
93+
94+
// Another mock server for the LLM agent API.
95+
srv=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
96+
iftest.status!=nil {
97+
httpapi.Write(context.Background(),w,http.StatusOK,test.status)
98+
}else {
99+
httpapi.Write(context.Background(),w,http.StatusBadRequest,nil)
100+
}
101+
}))
102+
t.Cleanup(srv.Close)
103+
agentapiURL:=srv.URL
104+
105+
inv,_:=clitest.New(t,"--agent-url",agentURL,"exp","task","report-status",
106+
"--app-slug","claude-code",
107+
"--agentapi-url",agentapiURL)
108+
stdout:=ptytest.New(t)
109+
inv.Stdout=stdout.Output()
110+
stderr:=ptytest.New(t)
111+
inv.Stderr=stderr.Output()
112+
113+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitShort)
114+
t.Cleanup(cancel)
115+
116+
gofunc() {
117+
err:=inv.WithContext(ctx).Run()
118+
assert.NoError(t,err)
119+
}()
120+
121+
// Should only try to update the status if we got one.
122+
iftest.status==nil {
123+
stderr.ExpectMatch("failed to fetch status")
124+
}else {
125+
got:=<-done
126+
require.Equal(t,got,test.expected)
127+
}
128+
129+
// Non-nil for the update means there was an error.
130+
iftest.resp!=nil {
131+
stderr.ExpectMatch("failed to update status")
132+
}
133+
})
134+
}
135+
}

‎go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ require (
485485

486486
require (
487487
github.com/anthropics/anthropic-sdk-gov0.2.0-beta.3
488+
github.com/coder/agentapi-sdk-gov0.0.0-20250505131810-560d1d88d225
488489
github.com/coder/previewv0.0.2-0.20250604144457-c9862a17f652
489490
github.com/fsnotify/fsnotifyv1.9.0
490491
github.com/kylecarbs/aisdk-gov0.0.8
@@ -523,6 +524,7 @@ require (
523524
github.com/samber/lov1.50.0// indirect
524525
github.com/spiffe/go-spiffe/v2v2.5.0// indirect
525526
github.com/tidwall/sjsonv1.2.5// indirect
527+
github.com/tmaxmax/go-ssev0.10.0// indirect
526528
github.com/ulikunitz/xzv0.5.12// indirect
527529
github.com/yosida95/uritemplate/v3v3.0.2// indirect
528530
github.com/zeebo/xxh3v1.0.2// indirect

‎go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,8 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH
894894
github.com/cncf/xds/gov0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
895895
github.com/cncf/xds/gov0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
896896
github.com/cncf/xds/gov0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
897+
github.com/coder/agentapi-sdk-gov0.0.0-20250505131810-560d1d88d225 h1:tRIViZ5JRmzdOEo5wUWngaGEFBG8OaE1o2GIHN5ujJ8=
898+
github.com/coder/agentapi-sdk-gov0.0.0-20250505131810-560d1d88d225/go.mod h1:rNLVpYgEVeu1Zk29K64z6Od8RBP9DwqCu9OfCzh8MR4=
897899
github.com/coder/bubbleteav1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI=
898900
github.com/coder/bubbleteav1.2.2-0.20241212190825-007a1cdb2c41/go.mod h1:I9ULxr64UaOSUv7hcb3nX4kowodJCVS7vt7VVJk/kW4=
899901
github.com/coder/clistatv1.0.0 h1:MjiS7qQ1IobuSSgDnxcCSyBPESs44hExnh2TEqMcGnA=
@@ -1827,6 +1829,8 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
18271829
github.com/tklauser/go-sysconfv0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
18281830
github.com/tklauser/numcpusv0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
18291831
github.com/tklauser/numcpusv0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
1832+
github.com/tmaxmax/go-ssev0.10.0 h1:j9F93WB4Hxt8wUf6oGffMm4dutALvUPoDDxfuDQOSqA=
1833+
github.com/tmaxmax/go-ssev0.10.0/go.mod h1:u/2kZQR1tyngo1lKaNCj1mJmhXGZWS1Zs5yiSOD+Eg8=
18301834
github.com/u-root/gobusybox/srcv0.0.0-20240225013946-a274a8d5d83a h1:eg5FkNoQp76ZsswyGZ+TjYqA/rhKefxK8BW7XOlQsxo=
18311835
github.com/u-root/gobusybox/srcv0.0.0-20240225013946-a274a8d5d83a/go.mod h1:e/8TmrdreH0sZOw2DFKBaUV7bvDWRq6SeM9PzkuVM68=
18321836
github.com/u-root/u-rootv0.14.0 h1:Ka4T10EEML7dQ5XDvO9c3MBN8z4nuSnGjcd1jmU2ivg=

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp