Skip to content

Commit 97b6d8c

Browse files
committed
feat(execd): add command/session resume api
1 parent 6893583 commit 97b6d8c

6 files changed

Lines changed: 40 additions & 7 deletions

File tree

components/execd/pkg/runtime/types.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ import (
2424
)
2525

2626
// ExecuteResultHook groups execution callbacks.
27-
// Eid is only assigned for stdout/stderr on run command (pipe tail) and run-in-session (bash stdout pipe);
28-
// other paths pass eid=0. Ids are allocated in runtime at pipe sync, not in HTTP/SSE writers.
2927
type ExecuteResultHook struct {
3028
OnExecuteInit func(context string)
3129
OnExecuteResult func(result map[string]any, count int)
@@ -48,20 +46,16 @@ type ExecuteCodeRequest struct {
4846
Gid *uint32 `json:"gid,omitempty"`
4947
Hooks ExecuteResultHook
5048

51-
// eventSeq assigns monotonic eids (1-based) for stdout/stderr on run command and bash session only.
5249
eventSeq atomic.Uint64
5350
}
5451

55-
// nextStdoutStderrEventID returns the next eid for stdout/stderr lines. Used only from run command
56-
// pipe tailers and bash session stdout; other callers should pass 0 into OnExecuteStdout/Stderr.
5752
func (req *ExecuteCodeRequest) nextStdoutStderrEventID() int64 {
5853
if req == nil {
5954
return 0
6055
}
6156
return int64(req.eventSeq.Add(1))
6257
}
6358

64-
// wrapStdoutPipeHook wraps stdout delivery so eid is assigned when a line is flushed from the pipe tailer, not in SSE writes.
6559
func (req *ExecuteCodeRequest) wrapStdoutPipeHook() func(string) {
6660
return func(text string) {
6761
if text == "" || req.Hooks.OnExecuteStdout == nil {
@@ -72,7 +66,6 @@ func (req *ExecuteCodeRequest) wrapStdoutPipeHook() func(string) {
7266
}
7367
}
7468

75-
// wrapStderrPipeHook wraps stderr delivery so eid is assigned when a line is flushed from the pipe tailer, not in SSE writes.
7669
func (req *ExecuteCodeRequest) wrapStderrPipeHook() func(string) {
7770
return func(text string) {
7871
if text == "" || req.Hooks.OnExecuteStderr == nil {

components/execd/pkg/web/controller/codeinterpreting.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,25 @@ func (c *CodeInterpretingController) RunInSession() {
321321
time.Sleep(flag.ApiGracefulShutdownTimeout)
322322
}
323323

324+
func (c *CodeInterpretingController) ResumeSessionStream() {
325+
sessionID := c.ctx.Param("sessionId")
326+
if sessionID == "" {
327+
c.RespondError(
328+
http.StatusBadRequest,
329+
model.ErrorCodeMissingQuery,
330+
"missing path parameter 'sessionId'",
331+
)
332+
return
333+
}
334+
_ = c.QueryInt64(c.ctx.Query(model.SessionResumeAfterEidQuery), 0)
335+
336+
c.RespondError(
337+
http.StatusNotImplemented,
338+
model.ErrorCodeNotImplemented,
339+
"session stream resume is not implemented yet",
340+
)
341+
}
342+
324343
// DeleteSession deletes a bash session (delete_session API).
325344
func (c *CodeInterpretingController) DeleteSession() {
326345
sessionID := c.ctx.Param("sessionId")

components/execd/pkg/web/controller/command.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ func (c *CodeInterpretingController) GetBackgroundCommandOutput() {
125125
c.ctx.String(http.StatusOK, "%s", output)
126126
}
127127

128+
func (c *CodeInterpretingController) ResumeCommandStream() {
129+
commandID := c.ctx.Param("id")
130+
if commandID == "" {
131+
c.RespondError(http.StatusBadRequest, model.ErrorCodeInvalidRequest, "missing command execution id")
132+
return
133+
}
134+
_ = c.QueryInt64(c.ctx.Query(model.CommandResumeAfterEidQuery), 0)
135+
136+
c.RespondError(
137+
http.StatusNotImplemented,
138+
model.ErrorCodeNotImplemented,
139+
"command stream resume is not implemented yet",
140+
)
141+
}
142+
128143
func (c *CodeInterpretingController) buildExecuteCommandRequest(request model.RunCommandRequest) *runtime.ExecuteCodeRequest {
129144
timeout := time.Duration(request.TimeoutMs) * time.Millisecond
130145
if request.Background {

components/execd/pkg/web/model/command.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import (
2121
"github.com/go-playground/validator/v10"
2222
)
2323

24+
const CommandResumeAfterEidQuery = "after_eid"
25+
const SessionResumeAfterEidQuery = CommandResumeAfterEidQuery
26+
2427
// RunCommandRequest represents a shell command execution request.
2528
type RunCommandRequest struct {
2629
Command string `json:"command" validate:"required"`

components/execd/pkg/web/model/error.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
ErrorCodeFileNotFound ErrorCode = "FILE_NOT_FOUND"
2727
ErrorCodeUnknown ErrorCode = "UNKNOWN"
2828
ErrorCodeContextNotFound ErrorCode = "CONTEXT_NOT_FOUND"
29+
ErrorCodeNotImplemented ErrorCode = "NOT_IMPLEMENTED"
2930
)
3031

3132
type ErrorResponse struct {

components/execd/pkg/web/router.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func NewRouter(accessToken string) *gin.Engine {
6666
{
6767
session.POST("", withCode(func(c *controller.CodeInterpretingController) { c.CreateSession() }))
6868
session.POST("/:sessionId/run", withCode(func(c *controller.CodeInterpretingController) { c.RunInSession() }))
69+
session.GET("/:sessionId/resume", withCode(func(c *controller.CodeInterpretingController) { c.ResumeSessionStream() }))
6970
session.DELETE("/:sessionId", withCode(func(c *controller.CodeInterpretingController) { c.DeleteSession() }))
7071
}
7172

@@ -74,6 +75,7 @@ func NewRouter(accessToken string) *gin.Engine {
7475
command.POST("", withCode(func(c *controller.CodeInterpretingController) { c.RunCommand() }))
7576
command.DELETE("", withCode(func(c *controller.CodeInterpretingController) { c.InterruptCommand() }))
7677
command.GET("/status/:id", withCode(func(c *controller.CodeInterpretingController) { c.GetCommandStatus() }))
78+
command.GET("/:id/resume", withCode(func(c *controller.CodeInterpretingController) { c.ResumeCommandStream() }))
7779
command.GET("/:id/logs", withCode(func(c *controller.CodeInterpretingController) { c.GetBackgroundCommandOutput() }))
7880
}
7981

0 commit comments

Comments
 (0)