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

Commitb269f85

Browse files
committed
feat(cli): add coder exp tasks list
Fixes#892,#896
1 parent427b23f commitb269f85

File tree

4 files changed

+190
-22
lines changed

4 files changed

+190
-22
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.tasksCommand(),
1920
},
2021
}
2122
returncmd

‎cli/exp_tasks.go‎

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"golang.org/x/xerrors"
9+
10+
"github.com/coder/coder/v2/cli/cliui"
11+
"github.com/coder/coder/v2/codersdk"
12+
"github.com/coder/serpent"
13+
)
14+
15+
func (r*RootCmd)tasksCommand()*serpent.Command {
16+
cmd:=&serpent.Command{
17+
Use:"task",
18+
Aliases: []string{"tasks"},
19+
Short:"Experimental task commands.",
20+
Handler:func(i*serpent.Invocation)error {
21+
returni.Command.HelpHandler(i)
22+
},
23+
Children: []*serpent.Command{
24+
r.tasksListCmd(),
25+
},
26+
}
27+
returncmd
28+
}
29+
30+
typetaskListRowstruct {
31+
Task codersdk.Task`table:"t,recursive_inline"`
32+
33+
StateChangedstring`table:"state changed"`
34+
}
35+
36+
functaskListRowFromTask(now time.Time,t codersdk.Task)taskListRow {
37+
varstateAgostring
38+
ift.CurrentState!=nil {
39+
stateAgo=now.UTC().Sub(t.CurrentState.Timestamp).Truncate(time.Second).String()+" ago"
40+
}
41+
42+
returntaskListRow{
43+
Task:t,
44+
45+
StateChanged:stateAgo,
46+
}
47+
}
48+
49+
func (r*RootCmd)tasksListCmd()*serpent.Command {
50+
var (
51+
statusFilterstring
52+
allbool
53+
userstring
54+
55+
client=new(codersdk.Client)
56+
formatter=cliui.NewOutputFormatter(
57+
cliui.TableFormat(
58+
[]taskListRow{},
59+
[]string{
60+
"id",
61+
"name",
62+
"status",
63+
"state",
64+
"state changed",
65+
"message",
66+
},
67+
),
68+
cliui.ChangeFormatterData(
69+
cliui.JSONFormat(),
70+
func(dataany) (any,error) {
71+
rows,ok:=data.([]taskListRow)
72+
if!ok {
73+
returnnil,xerrors.Errorf("expected []taskListRow, got %T",data)
74+
}
75+
out:=make([]codersdk.Task,len(rows))
76+
fori:=rangerows {
77+
out[i]=rows[i].Task
78+
}
79+
returnout,nil
80+
},
81+
),
82+
)
83+
)
84+
85+
cmd:=&serpent.Command{
86+
Use:"list",
87+
Short:"List experimental tasks",
88+
Aliases: []string{"ls"},
89+
Middleware:serpent.Chain(
90+
serpent.RequireNArgs(0),
91+
r.InitClient(client),
92+
),
93+
Options: serpent.OptionSet{
94+
{
95+
Name:"status",
96+
Description:"Filter by task status (e.g. running, failed, etc).",
97+
Flag:"status",
98+
Default:"",
99+
Value:serpent.StringOf(&statusFilter),
100+
},
101+
{
102+
Name:"all",
103+
Description:"List tasks for all users you can view.",
104+
Flag:"all",
105+
FlagShorthand:"a",
106+
Default:"false",
107+
Value:serpent.BoolOf(&all),
108+
},
109+
{
110+
Name:"user",
111+
Description:"List tasks for the specified user (username,\"me\").",
112+
Flag:"user",
113+
Default:"",
114+
Value:serpent.StringOf(&user),
115+
},
116+
},
117+
Handler:func(inv*serpent.Invocation)error {
118+
ctx:=inv.Context()
119+
exp:=codersdk.NewExperimentalClient(client)
120+
121+
targetUser:=strings.TrimSpace(user)
122+
iftargetUser==""&&!all {
123+
targetUser=codersdk.Me
124+
}
125+
126+
tasks,err:=exp.Tasks(ctx,&codersdk.TasksFilter{
127+
Owner:targetUser,
128+
Status:statusFilter,
129+
})
130+
iferr!=nil {
131+
returnxerrors.Errorf("list tasks: %w",err)
132+
}
133+
134+
// If no rows and not JSON, show a friendly message.
135+
iflen(tasks)==0&&formatter.FormatID()!=cliui.JSONFormat().ID() {
136+
_,_=fmt.Fprintln(inv.Stderr,"No tasks found.")
137+
returnnil
138+
}
139+
140+
rows:=make([]taskListRow,len(tasks))
141+
now:=time.Now()
142+
fori:=rangetasks {
143+
rows[i]=taskListRowFromTask(now,tasks[i])
144+
}
145+
146+
out,err:=formatter.Format(ctx,rows)
147+
iferr!=nil {
148+
returnxerrors.Errorf("format tasks: %w",err)
149+
}
150+
_,_=fmt.Fprintln(inv.Stdout,out)
151+
returnnil
152+
},
153+
}
154+
155+
formatter.AttachOptions(&cmd.Options)
156+
returncmd
157+
}

‎coderd/coderd.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,10 +1008,10 @@ func New(options *Options) *API {
10081008
r.Route("/tasks",func(r chi.Router) {
10091009
r.Use(apiRateLimiter)
10101010

1011+
r.Get("/",api.tasksList)
1012+
10111013
r.Route("/{user}",func(r chi.Router) {
10121014
r.Use(httpmw.ExtractOrganizationMembersParam(options.Database,api.HTTPAuth.Authorize))
1013-
1014-
r.Get("/",api.tasksList)
10151015
r.Get("/{id}",api.taskGet)
10161016
r.Post("/",api.tasksCreate)
10171017
})

‎codersdk/aitasks.go‎

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,35 +88,41 @@ const (
8888
//
8989
// Experimental: This type is experimental and may change in the future.
9090
typeTaskstruct {
91-
ID uuid.UUID`json:"id" format:"uuid"`
92-
OrganizationID uuid.UUID`json:"organization_id" format:"uuid"`
93-
OwnerID uuid.UUID`json:"owner_id" format:"uuid"`
94-
Namestring`json:"name"`
95-
TemplateID uuid.UUID`json:"template_id" format:"uuid"`
96-
WorkspaceID uuid.NullUUID`json:"workspace_id" format:"uuid"`
97-
InitialPromptstring`json:"initial_prompt"`
98-
StatusWorkspaceStatus`json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted"`
99-
CurrentState*TaskStateEntry`json:"current_state"`
100-
CreatedAt time.Time`json:"created_at" format:"date-time"`
101-
UpdatedAt time.Time`json:"updated_at" format:"date-time"`
91+
ID uuid.UUID`json:"id" format:"uuid" table:"id"`
92+
OrganizationID uuid.UUID`json:"organization_id" format:"uuid" table:"organization id"`
93+
OwnerID uuid.UUID`json:"owner_id" format:"uuid" table:"owner id"`
94+
Namestring`json:"name" table:"name,default_sort"`
95+
TemplateID uuid.UUID`json:"template_id" format:"uuid" table:"template id"`
96+
WorkspaceID uuid.NullUUID`json:"workspace_id" format:"uuid" table:"workspace id"`
97+
InitialPromptstring`json:"initial_prompt" table:"initial prompt"`
98+
StatusWorkspaceStatus`json:"status" enums:"pending,starting,running,stopping,stopped,failed,canceling,canceled,deleting,deleted" table:"status"`
99+
CurrentState*TaskStateEntry`json:"current_state" table:"cs,recursive_inline"`
100+
CreatedAt time.Time`json:"created_at" format:"date-time" table:"created at"`
101+
UpdatedAt time.Time`json:"updated_at" format:"date-time" table:"updated at"`
102102
}
103103

104104
// TaskStateEntry represents a single entry in the task's state history.
105105
//
106106
// Experimental: This type is experimental and may change in the future.
107107
typeTaskStateEntrystruct {
108-
Timestamp time.Time`json:"timestamp" format:"date-time"`
109-
StateTaskState`json:"state" enum:"working,idle,completed,failed"`
110-
Messagestring`json:"message"`
111-
URIstring`json:"uri"`
108+
Timestamp time.Time`json:"timestamp" format:"date-time" table:"-"`
109+
StateTaskState`json:"state" enum:"working,idle,completed,failed" table:"state"`
110+
Messagestring`json:"message" table:"message"`
111+
URIstring`json:"uri" table:"-"`
112112
}
113113

114114
// TasksFilter filters the list of tasks.
115115
//
116116
// Experimental: This type is experimental and may change in the future.
117117
typeTasksFilterstruct {
118-
// Owner can be a username, UUID, or "me"
118+
// Owner can be a username, UUID, or "me".
119119
Ownerstring`json:"owner,omitempty"`
120+
// Status is a task status.
121+
Statusstring`json:"status,omitempty" typescript:"-"`
122+
// Offset is the number of tasks to skip before returning results.
123+
Offsetint`json:"offset,omitempty" typescript:"-"`
124+
// Limit is a limit on the number of tasks returned.
125+
Limitint`json:"limit,omitempty" typescript:"-"`
120126
}
121127

122128
// Tasks lists all tasks belonging to the user or specified owner.
@@ -126,12 +132,16 @@ func (c *ExperimentalClient) Tasks(ctx context.Context, filter *TasksFilter) ([]
126132
iffilter==nil {
127133
filter=&TasksFilter{}
128134
}
129-
user:=filter.Owner
130-
ifuser=="" {
131-
user="me"
135+
136+
varwsFilterWorkspaceFilter
137+
wsFilter.Owner=filter.Owner
138+
wsFilter.Status=filter.Status
139+
page:=Pagination{
140+
Offset:filter.Offset,
141+
Limit:filter.Limit,
132142
}
133143

134-
res,err:=c.Request(ctx,http.MethodGet,fmt.Sprintf("/api/experimental/tasks/%s",user),nil)
144+
res,err:=c.Request(ctx,http.MethodGet,"/api/experimental/tasks",nil,wsFilter.asRequestOption(),page.asRequestOption())
135145
iferr!=nil {
136146
returnnil,err
137147
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp