mirror of
https://github.com/block/goose.git
synced 2026-07-10 00:20:17 +00:00
feat (acp): custom methods for managing scheduler (#9972)
This commit is contained in:
parent
09998b12cf
commit
eea698945c
28 changed files with 2050 additions and 484 deletions
|
|
@ -6,6 +6,8 @@ use std::collections::HashMap;
|
|||
|
||||
mod recipe;
|
||||
pub use recipe::*;
|
||||
mod schedule;
|
||||
pub use schedule::*;
|
||||
|
||||
/// Schema descriptor for a single custom method, produced by the
|
||||
/// `#[custom_methods]` macro's generated `custom_method_schemas()` function.
|
||||
|
|
|
|||
169
crates/goose-sdk-types/src/custom_requests/schedule.rs
Normal file
169
crates/goose-sdk-types/src/custom_requests/schedule.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
use agent_client_protocol::schema::SessionInfo;
|
||||
use agent_client_protocol::{JsonRpcRequest, JsonRpcResponse};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{EmptyResponse, RecipeDto};
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ScheduledJobDto {
|
||||
pub id: String,
|
||||
pub source: String,
|
||||
pub cron: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub last_run: Option<String>,
|
||||
pub currently_running: bool,
|
||||
pub paused: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub current_session_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub job_start_time: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/list",
|
||||
response = ListSchedulesResponse
|
||||
)]
|
||||
pub struct ListSchedulesRequest {}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct ListSchedulesResponse {
|
||||
pub jobs: Vec<ScheduledJobDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/create",
|
||||
response = CreateScheduleResponse
|
||||
)]
|
||||
pub struct CreateScheduleRequest {
|
||||
pub id: String,
|
||||
pub recipe: RecipeDto,
|
||||
pub cron: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct CreateScheduleResponse {
|
||||
pub job: ScheduledJobDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(method = "_goose/unstable/schedules/delete", response = EmptyResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteScheduleRequest {
|
||||
pub schedule_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/update",
|
||||
response = UpdateScheduleResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateScheduleRequest {
|
||||
pub schedule_id: String,
|
||||
pub cron: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct UpdateScheduleResponse {
|
||||
pub job: ScheduledJobDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/run-now",
|
||||
response = RunScheduleNowResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RunScheduleNowRequest {
|
||||
pub schedule_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RunScheduleNowResponse {
|
||||
pub status: RunScheduleNowStatus,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RunScheduleNowStatus {
|
||||
#[default]
|
||||
Completed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/sessions/list",
|
||||
response = ListScheduleSessionsResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ListScheduleSessionsRequest {
|
||||
pub schedule_id: String,
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct ListScheduleSessionsResponse {
|
||||
pub sessions: Vec<SessionInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(method = "_goose/unstable/schedules/pause", response = EmptyResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PauseScheduleRequest {
|
||||
pub schedule_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/unpause",
|
||||
response = EmptyResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UnpauseScheduleRequest {
|
||||
pub schedule_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/running-job/kill",
|
||||
response = KillRunningJobResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct KillRunningJobRequest {
|
||||
pub job_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct KillRunningJobResponse {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(
|
||||
method = "_goose/unstable/schedules/running-job/inspect",
|
||||
response = InspectRunningJobResponse
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InspectRunningJobRequest {
|
||||
pub job_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InspectRunningJobResponse {
|
||||
pub running: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub session_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub job_start_time: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub running_duration_seconds: Option<i64>,
|
||||
}
|
||||
|
|
@ -250,6 +250,56 @@
|
|||
"requestType": "RecipeToYamlRequest_unstable",
|
||||
"responseType": "RecipeToYamlResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/list",
|
||||
"requestType": "ListSchedulesRequest_unstable",
|
||||
"responseType": "ListSchedulesResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/sessions/list",
|
||||
"requestType": "ListScheduleSessionsRequest_unstable",
|
||||
"responseType": "ListScheduleSessionsResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/create",
|
||||
"requestType": "CreateScheduleRequest_unstable",
|
||||
"responseType": "CreateScheduleResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/delete",
|
||||
"requestType": "DeleteScheduleRequest_unstable",
|
||||
"responseType": "EmptyResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/pause",
|
||||
"requestType": "PauseScheduleRequest_unstable",
|
||||
"responseType": "EmptyResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/unpause",
|
||||
"requestType": "UnpauseScheduleRequest_unstable",
|
||||
"responseType": "EmptyResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/update",
|
||||
"requestType": "UpdateScheduleRequest_unstable",
|
||||
"responseType": "UpdateScheduleResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/run-now",
|
||||
"requestType": "RunScheduleNowRequest_unstable",
|
||||
"responseType": "RunScheduleNowResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/running-job/kill",
|
||||
"requestType": "KillRunningJobRequest_unstable",
|
||||
"responseType": "KillRunningJobResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/schedules/running-job/inspect",
|
||||
"requestType": "InspectRunningJobRequest_unstable",
|
||||
"responseType": "InspectRunningJobResponse_unstable"
|
||||
},
|
||||
{
|
||||
"method": "_goose/unstable/session/info",
|
||||
"requestType": "GetSessionInfoRequest_unstable",
|
||||
|
|
|
|||
|
|
@ -3520,32 +3520,105 @@
|
|||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/recipes/to-yaml"
|
||||
},
|
||||
"GetSessionInfoRequest_unstable": {
|
||||
"ListSchedulesRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionId"
|
||||
],
|
||||
"description": "Return list-style metadata for a single session without loading the conversation.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/info"
|
||||
"x-method": "_goose/unstable/schedules/list"
|
||||
},
|
||||
"GetSessionInfoResponse_unstable": {
|
||||
"ListSchedulesResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session": {
|
||||
"$ref": "#/$defs/SessionInfo"
|
||||
"jobs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/ScheduledJobDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"session"
|
||||
"jobs"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/info"
|
||||
"x-method": "_goose/unstable/schedules/list"
|
||||
},
|
||||
"ScheduledJobDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastRun": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"currentlyRunning": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"paused": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"currentSessionId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"jobStartTime": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"source",
|
||||
"cron",
|
||||
"currentlyRunning",
|
||||
"paused"
|
||||
]
|
||||
},
|
||||
"ListScheduleSessionsRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId",
|
||||
"limit"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/sessions/list"
|
||||
},
|
||||
"ListScheduleSessionsResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/SessionInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessions"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/sessions/list"
|
||||
},
|
||||
"SessionInfo": {
|
||||
"type": "object",
|
||||
|
|
@ -3598,6 +3671,245 @@
|
|||
"type": "string",
|
||||
"description": "A unique identifier for a conversation session between a client and agent.\n\nSessions maintain their own context, conversation history, and state,\nallowing multiple independent interactions with the same agent.\n\nSee protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session-setup#session-id)"
|
||||
},
|
||||
"CreateScheduleRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe": {
|
||||
"$ref": "#/$defs/RecipeDto"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"recipe",
|
||||
"cron"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/create"
|
||||
},
|
||||
"CreateScheduleResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job": {
|
||||
"$ref": "#/$defs/ScheduledJobDto"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"job"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/create"
|
||||
},
|
||||
"DeleteScheduleRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/delete"
|
||||
},
|
||||
"PauseScheduleRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/pause"
|
||||
},
|
||||
"UnpauseScheduleRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/unpause"
|
||||
},
|
||||
"UpdateScheduleRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId",
|
||||
"cron"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/update"
|
||||
},
|
||||
"UpdateScheduleResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job": {
|
||||
"$ref": "#/$defs/ScheduledJobDto"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"job"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/update"
|
||||
},
|
||||
"RunScheduleNowRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scheduleId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"scheduleId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/run-now"
|
||||
},
|
||||
"RunScheduleNowResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"$ref": "#/$defs/RunScheduleNowStatus"
|
||||
},
|
||||
"sessionId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/run-now"
|
||||
},
|
||||
"RunScheduleNowStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"completed",
|
||||
"cancelled"
|
||||
]
|
||||
},
|
||||
"KillRunningJobRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jobId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"jobId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/running-job/kill"
|
||||
},
|
||||
"KillRunningJobResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/running-job/kill"
|
||||
},
|
||||
"InspectRunningJobRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jobId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"jobId"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/running-job/inspect"
|
||||
},
|
||||
"InspectRunningJobResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"running": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sessionId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"jobStartTime": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"runningDurationSeconds": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"running"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/schedules/running-job/inspect"
|
||||
},
|
||||
"GetSessionInfoRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionId"
|
||||
],
|
||||
"description": "Return list-style metadata for a single session without loading the conversation.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/info"
|
||||
},
|
||||
"GetSessionInfoResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session": {
|
||||
"$ref": "#/$defs/SessionInfo"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"session"
|
||||
],
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/info"
|
||||
},
|
||||
"TruncateSessionConversationRequest_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -5195,6 +5507,96 @@
|
|||
"description": "Params for _goose/unstable/recipes/to-yaml",
|
||||
"title": "RecipeToYamlRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ListSchedulesRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/list",
|
||||
"title": "ListSchedulesRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ListScheduleSessionsRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/sessions/list",
|
||||
"title": "ListScheduleSessionsRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/CreateScheduleRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/create",
|
||||
"title": "CreateScheduleRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/DeleteScheduleRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/delete",
|
||||
"title": "DeleteScheduleRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/PauseScheduleRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/pause",
|
||||
"title": "PauseScheduleRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/UnpauseScheduleRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/unpause",
|
||||
"title": "UnpauseScheduleRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/UpdateScheduleRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/update",
|
||||
"title": "UpdateScheduleRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/RunScheduleNowRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/run-now",
|
||||
"title": "RunScheduleNowRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/KillRunningJobRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/running-job/kill",
|
||||
"title": "KillRunningJobRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/InspectRunningJobRequest_unstable"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/unstable/schedules/running-job/inspect",
|
||||
"title": "InspectRunningJobRequest_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
|
|
@ -5721,6 +6123,62 @@
|
|||
],
|
||||
"title": "RecipeToYamlResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ListSchedulesResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "ListSchedulesResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ListScheduleSessionsResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "ListScheduleSessionsResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/CreateScheduleResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "CreateScheduleResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/UpdateScheduleResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "UpdateScheduleResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/RunScheduleNowResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "RunScheduleNowResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/KillRunningJobResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "KillRunningJobResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/InspectRunningJobResponse_unstable"
|
||||
}
|
||||
],
|
||||
"title": "InspectRunningJobResponse_unstable"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ mod onboarding;
|
|||
mod providers;
|
||||
mod recipe;
|
||||
mod resources;
|
||||
mod schedule;
|
||||
mod slash_commands;
|
||||
mod sources;
|
||||
mod tool_notifications;
|
||||
|
|
|
|||
|
|
@ -416,6 +416,86 @@ impl GooseAcpAgent {
|
|||
self.on_recipe_to_yaml(req).await
|
||||
}
|
||||
|
||||
#[custom_method(ListSchedulesRequest)]
|
||||
async fn dispatch_list_schedules(
|
||||
&self,
|
||||
req: ListSchedulesRequest,
|
||||
) -> Result<ListSchedulesResponse, agent_client_protocol::Error> {
|
||||
self.on_list_schedules(req).await
|
||||
}
|
||||
|
||||
#[custom_method(ListScheduleSessionsRequest)]
|
||||
async fn dispatch_list_schedule_sessions(
|
||||
&self,
|
||||
req: ListScheduleSessionsRequest,
|
||||
) -> Result<ListScheduleSessionsResponse, agent_client_protocol::Error> {
|
||||
self.on_list_schedule_sessions(req).await
|
||||
}
|
||||
|
||||
#[custom_method(CreateScheduleRequest)]
|
||||
async fn dispatch_create_schedule(
|
||||
&self,
|
||||
req: CreateScheduleRequest,
|
||||
) -> Result<CreateScheduleResponse, agent_client_protocol::Error> {
|
||||
self.on_create_schedule(req).await
|
||||
}
|
||||
|
||||
#[custom_method(DeleteScheduleRequest)]
|
||||
async fn dispatch_delete_schedule(
|
||||
&self,
|
||||
req: DeleteScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.on_delete_schedule(req).await
|
||||
}
|
||||
|
||||
#[custom_method(PauseScheduleRequest)]
|
||||
async fn dispatch_pause_schedule(
|
||||
&self,
|
||||
req: PauseScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.on_pause_schedule(req).await
|
||||
}
|
||||
|
||||
#[custom_method(UnpauseScheduleRequest)]
|
||||
async fn dispatch_unpause_schedule(
|
||||
&self,
|
||||
req: UnpauseScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.on_unpause_schedule(req).await
|
||||
}
|
||||
|
||||
#[custom_method(UpdateScheduleRequest)]
|
||||
async fn dispatch_update_schedule(
|
||||
&self,
|
||||
req: UpdateScheduleRequest,
|
||||
) -> Result<UpdateScheduleResponse, agent_client_protocol::Error> {
|
||||
self.on_update_schedule(req).await
|
||||
}
|
||||
|
||||
#[custom_method(RunScheduleNowRequest)]
|
||||
async fn dispatch_run_schedule_now(
|
||||
&self,
|
||||
req: RunScheduleNowRequest,
|
||||
) -> Result<RunScheduleNowResponse, agent_client_protocol::Error> {
|
||||
self.on_run_schedule_now(req).await
|
||||
}
|
||||
|
||||
#[custom_method(KillRunningJobRequest)]
|
||||
async fn dispatch_kill_running_job(
|
||||
&self,
|
||||
req: KillRunningJobRequest,
|
||||
) -> Result<KillRunningJobResponse, agent_client_protocol::Error> {
|
||||
self.on_kill_running_job(req).await
|
||||
}
|
||||
|
||||
#[custom_method(InspectRunningJobRequest)]
|
||||
async fn dispatch_inspect_running_job(
|
||||
&self,
|
||||
req: InspectRunningJobRequest,
|
||||
) -> Result<InspectRunningJobResponse, agent_client_protocol::Error> {
|
||||
self.on_inspect_running_job(req).await
|
||||
}
|
||||
|
||||
#[custom_method(GetSessionInfoRequest)]
|
||||
async fn dispatch_get_session_info(
|
||||
&self,
|
||||
|
|
|
|||
343
crates/goose/src/acp/server/schedule.rs
Normal file
343
crates/goose/src/acp/server/schedule.rs
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
use goose_sdk_types::custom_requests::{
|
||||
CreateScheduleRequest, CreateScheduleResponse, DeleteScheduleRequest, EmptyResponse,
|
||||
InspectRunningJobRequest, InspectRunningJobResponse, KillRunningJobRequest,
|
||||
KillRunningJobResponse, ListScheduleSessionsRequest, ListScheduleSessionsResponse,
|
||||
ListSchedulesRequest, ListSchedulesResponse, PauseScheduleRequest, RunScheduleNowRequest,
|
||||
RunScheduleNowResponse, RunScheduleNowStatus, ScheduledJobDto, UnpauseScheduleRequest,
|
||||
UpdateScheduleRequest, UpdateScheduleResponse,
|
||||
};
|
||||
use tokio::fs;
|
||||
|
||||
use super::{build_session_info, GooseAcpAgent, ResultExt};
|
||||
use crate::recipe::validate_recipe::validate_recipe_template_from_content;
|
||||
use crate::recipe::Recipe;
|
||||
use crate::scheduler::{get_default_scheduled_recipes_dir, ScheduledJob, SchedulerError};
|
||||
|
||||
fn validate_schedule_id(id: &str) -> Result<(), agent_client_protocol::Error> {
|
||||
let is_valid = !id.is_empty()
|
||||
&& id
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == ' ');
|
||||
|
||||
if !is_valid {
|
||||
return Err(agent_client_protocol::Error::invalid_params().data(
|
||||
"Schedule name must use only alphanumeric characters, hyphens, underscores, or spaces",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_schedule_recipe(recipe: &Recipe) -> Result<(), agent_client_protocol::Error> {
|
||||
let recipe_yaml = recipe
|
||||
.to_yaml()
|
||||
.map_err(|e| agent_client_protocol::Error::invalid_params().data(e.to_string()))?;
|
||||
|
||||
validate_recipe_template_from_content(&recipe_yaml, None)
|
||||
.map_err(|e| agent_client_protocol::Error::invalid_params().data(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_not_found_or_internal(error: SchedulerError) -> agent_client_protocol::Error {
|
||||
match error {
|
||||
SchedulerError::JobNotFound(id) => {
|
||||
agent_client_protocol::Error::resource_not_found(Some(id))
|
||||
}
|
||||
error => agent_client_protocol::Error::internal_error().data(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_schedule_error(error: SchedulerError) -> agent_client_protocol::Error {
|
||||
match error {
|
||||
SchedulerError::CronParseError(message) => agent_client_protocol::Error::invalid_params()
|
||||
.data(format!("Invalid cron expression: {message}")),
|
||||
SchedulerError::RecipeLoadError(message) => agent_client_protocol::Error::invalid_params()
|
||||
.data(format!("Recipe load error: {message}")),
|
||||
SchedulerError::JobIdExists(id) => agent_client_protocol::Error::invalid_params()
|
||||
.data(format!("Job ID already exists: {id}")),
|
||||
error => agent_client_protocol::Error::internal_error()
|
||||
.data(format!("Error creating schedule: {error}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_state_error(error: SchedulerError) -> agent_client_protocol::Error {
|
||||
match error {
|
||||
SchedulerError::JobNotFound(id) => {
|
||||
agent_client_protocol::Error::resource_not_found(Some(id))
|
||||
}
|
||||
SchedulerError::AnyhowError(error) => {
|
||||
agent_client_protocol::Error::invalid_params().data(error.to_string())
|
||||
}
|
||||
error => agent_client_protocol::Error::internal_error().data(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_schedule_error(error: SchedulerError) -> agent_client_protocol::Error {
|
||||
match error {
|
||||
SchedulerError::JobNotFound(id) => {
|
||||
agent_client_protocol::Error::resource_not_found(Some(id))
|
||||
}
|
||||
SchedulerError::AnyhowError(error) => {
|
||||
agent_client_protocol::Error::invalid_params().data(error.to_string())
|
||||
}
|
||||
SchedulerError::CronParseError(message) => agent_client_protocol::Error::invalid_params()
|
||||
.data(format!("Invalid cron expression: {message}")),
|
||||
error => agent_client_protocol::Error::internal_error().data(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_schedule_now_error(
|
||||
error: SchedulerError,
|
||||
) -> Result<RunScheduleNowResponse, agent_client_protocol::Error> {
|
||||
match error {
|
||||
SchedulerError::JobNotFound(id) => {
|
||||
Err(agent_client_protocol::Error::resource_not_found(Some(id)))
|
||||
}
|
||||
SchedulerError::AnyhowError(error)
|
||||
if error.to_string().contains("was successfully cancelled") =>
|
||||
{
|
||||
Ok(RunScheduleNowResponse {
|
||||
status: RunScheduleNowStatus::Cancelled,
|
||||
session_id: None,
|
||||
})
|
||||
}
|
||||
error => Err(agent_client_protocol::Error::internal_error()
|
||||
.data(format!("Error running schedule: {error}"))),
|
||||
}
|
||||
}
|
||||
|
||||
fn scheduled_job_to_dto(job: ScheduledJob) -> ScheduledJobDto {
|
||||
ScheduledJobDto {
|
||||
id: job.id,
|
||||
source: job.source,
|
||||
cron: job.cron,
|
||||
last_run: job.last_run.map(|value| value.to_rfc3339()),
|
||||
currently_running: job.currently_running,
|
||||
paused: job.paused,
|
||||
current_session_id: job.current_session_id,
|
||||
job_start_time: job.process_start_time.map(|value| value.to_rfc3339()),
|
||||
}
|
||||
}
|
||||
|
||||
impl GooseAcpAgent {
|
||||
pub(super) async fn on_list_schedules(
|
||||
&self,
|
||||
_req: ListSchedulesRequest,
|
||||
) -> Result<ListSchedulesResponse, agent_client_protocol::Error> {
|
||||
let jobs = self
|
||||
.agent_manager
|
||||
.scheduler()
|
||||
.list_scheduled_jobs()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(scheduled_job_to_dto)
|
||||
.collect();
|
||||
|
||||
Ok(ListSchedulesResponse { jobs })
|
||||
}
|
||||
|
||||
pub(super) async fn on_list_schedule_sessions(
|
||||
&self,
|
||||
req: ListScheduleSessionsRequest,
|
||||
) -> Result<ListScheduleSessionsResponse, agent_client_protocol::Error> {
|
||||
let sessions = self
|
||||
.agent_manager
|
||||
.scheduler()
|
||||
.sessions(&req.schedule_id, req.limit)
|
||||
.await
|
||||
.internal_err_ctx("Failed to fetch schedule sessions")?
|
||||
.into_iter()
|
||||
.map(|(_, session)| build_session_info(session))
|
||||
.collect();
|
||||
|
||||
Ok(ListScheduleSessionsResponse { sessions })
|
||||
}
|
||||
|
||||
pub(super) async fn on_create_schedule(
|
||||
&self,
|
||||
req: CreateScheduleRequest,
|
||||
) -> Result<CreateScheduleResponse, agent_client_protocol::Error> {
|
||||
let id = req.id.trim().to_string();
|
||||
validate_schedule_id(&id)?;
|
||||
|
||||
let recipe = Recipe::try_from(req.recipe).map_err(|e| {
|
||||
agent_client_protocol::Error::invalid_params().data(format!("recipe: {e}"))
|
||||
})?;
|
||||
|
||||
if recipe.check_for_security_warnings() {
|
||||
return Err(agent_client_protocol::Error::invalid_params().data(
|
||||
"This recipe contains hidden characters that could be malicious. Please remove them before trying to save.",
|
||||
));
|
||||
}
|
||||
validate_schedule_recipe(&recipe)?;
|
||||
|
||||
let scheduled_recipes_dir = get_default_scheduled_recipes_dir().map_err(|e| {
|
||||
agent_client_protocol::Error::internal_error()
|
||||
.data(format!("Failed to get scheduled recipes directory: {e}"))
|
||||
})?;
|
||||
|
||||
let recipe_path = scheduled_recipes_dir.join(format!("{id}.yaml"));
|
||||
let yaml_content = recipe.to_yaml().map_err(|e| {
|
||||
agent_client_protocol::Error::internal_error()
|
||||
.data(format!("Failed to convert recipe to YAML: {e}"))
|
||||
})?;
|
||||
fs::write(&recipe_path, yaml_content).await.map_err(|e| {
|
||||
agent_client_protocol::Error::internal_error()
|
||||
.data(format!("Failed to save recipe file: {e}"))
|
||||
})?;
|
||||
|
||||
let job = ScheduledJob {
|
||||
id,
|
||||
source: recipe_path.to_string_lossy().into_owned(),
|
||||
cron: req.cron,
|
||||
last_run: None,
|
||||
currently_running: false,
|
||||
paused: false,
|
||||
current_session_id: None,
|
||||
process_start_time: None,
|
||||
parameters: vec![],
|
||||
recipe_base_dir: None,
|
||||
};
|
||||
|
||||
self.agent_manager
|
||||
.scheduler()
|
||||
.add_scheduled_job(job.clone(), false)
|
||||
.await
|
||||
.map_err(create_schedule_error)?;
|
||||
|
||||
Ok(CreateScheduleResponse {
|
||||
job: scheduled_job_to_dto(job),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) async fn on_delete_schedule(
|
||||
&self,
|
||||
req: DeleteScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.agent_manager
|
||||
.scheduler()
|
||||
.remove_scheduled_job(&req.schedule_id, false)
|
||||
.await
|
||||
.map_err(schedule_not_found_or_internal)?;
|
||||
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
pub(super) async fn on_pause_schedule(
|
||||
&self,
|
||||
req: PauseScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.agent_manager
|
||||
.scheduler()
|
||||
.pause_schedule(&req.schedule_id)
|
||||
.await
|
||||
.map_err(schedule_state_error)?;
|
||||
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
pub(super) async fn on_unpause_schedule(
|
||||
&self,
|
||||
req: UnpauseScheduleRequest,
|
||||
) -> Result<EmptyResponse, agent_client_protocol::Error> {
|
||||
self.agent_manager
|
||||
.scheduler()
|
||||
.unpause_schedule(&req.schedule_id)
|
||||
.await
|
||||
.map_err(schedule_not_found_or_internal)?;
|
||||
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
pub(super) async fn on_update_schedule(
|
||||
&self,
|
||||
req: UpdateScheduleRequest,
|
||||
) -> Result<UpdateScheduleResponse, agent_client_protocol::Error> {
|
||||
let schedule_id = req.schedule_id;
|
||||
let cron = req.cron;
|
||||
let scheduler = self.agent_manager.scheduler();
|
||||
scheduler
|
||||
.update_schedule(&schedule_id, cron)
|
||||
.await
|
||||
.map_err(update_schedule_error)?;
|
||||
|
||||
let job = scheduler
|
||||
.list_scheduled_jobs()
|
||||
.await
|
||||
.into_iter()
|
||||
.find(|job| job.id == schedule_id)
|
||||
.ok_or_else(|| {
|
||||
agent_client_protocol::Error::internal_error()
|
||||
.data("Schedule not found after update")
|
||||
})?;
|
||||
|
||||
Ok(UpdateScheduleResponse {
|
||||
job: scheduled_job_to_dto(job),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) async fn on_run_schedule_now(
|
||||
&self,
|
||||
req: RunScheduleNowRequest,
|
||||
) -> Result<RunScheduleNowResponse, agent_client_protocol::Error> {
|
||||
match self
|
||||
.agent_manager
|
||||
.scheduler()
|
||||
.run_now(&req.schedule_id)
|
||||
.await
|
||||
{
|
||||
Ok(session_id) => Ok(RunScheduleNowResponse {
|
||||
status: RunScheduleNowStatus::Completed,
|
||||
session_id: Some(session_id),
|
||||
}),
|
||||
Err(error) => run_schedule_now_error(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn on_kill_running_job(
|
||||
&self,
|
||||
req: KillRunningJobRequest,
|
||||
) -> Result<KillRunningJobResponse, agent_client_protocol::Error> {
|
||||
self.agent_manager
|
||||
.scheduler()
|
||||
.kill_running_job(&req.job_id)
|
||||
.await
|
||||
.map_err(schedule_state_error)?;
|
||||
|
||||
Ok(KillRunningJobResponse {
|
||||
message: format!("Successfully killed running job '{}'", req.job_id),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) async fn on_inspect_running_job(
|
||||
&self,
|
||||
req: InspectRunningJobRequest,
|
||||
) -> Result<InspectRunningJobResponse, agent_client_protocol::Error> {
|
||||
let job = self
|
||||
.agent_manager
|
||||
.scheduler()
|
||||
.list_scheduled_jobs()
|
||||
.await
|
||||
.into_iter()
|
||||
.find(|job| job.id == req.job_id)
|
||||
.ok_or_else(|| agent_client_protocol::Error::resource_not_found(Some(req.job_id)))?;
|
||||
|
||||
if !job.currently_running {
|
||||
return Ok(InspectRunningJobResponse::default());
|
||||
}
|
||||
|
||||
let running_duration_seconds = job.process_start_time.map(|start_time| {
|
||||
chrono::Utc::now()
|
||||
.signed_duration_since(start_time)
|
||||
.num_seconds()
|
||||
});
|
||||
|
||||
Ok(InspectRunningJobResponse {
|
||||
running: true,
|
||||
session_id: job.current_session_id,
|
||||
job_start_time: job.process_start_time.map(|value| value.to_rfc3339()),
|
||||
running_duration_seconds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -139,6 +139,16 @@ async fn persist_jobs(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_running_state(job: &mut ScheduledJob) -> bool {
|
||||
let changed = job.currently_running
|
||||
|| job.current_session_id.is_some()
|
||||
|| job.process_start_time.is_some();
|
||||
job.currently_running = false;
|
||||
job.current_session_id = None;
|
||||
job.process_start_time = None;
|
||||
changed
|
||||
}
|
||||
|
||||
pub struct Scheduler {
|
||||
tokio_scheduler: TokioJobScheduler,
|
||||
jobs: Arc<Mutex<JobsMap>>,
|
||||
|
|
@ -432,7 +442,7 @@ impl Scheduler {
|
|||
return;
|
||||
}
|
||||
|
||||
let list: Vec<ScheduledJob> = match serde_json::from_str(&data) {
|
||||
let mut list: Vec<ScheduledJob> = match serde_json::from_str(&data) {
|
||||
Ok(jobs) => jobs,
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
|
|
@ -444,6 +454,20 @@ impl Scheduler {
|
|||
}
|
||||
};
|
||||
|
||||
let reset_stale_running_state = list
|
||||
.iter_mut()
|
||||
.fold(false, |changed, job| clear_running_state(job) || changed);
|
||||
if reset_stale_running_state {
|
||||
match serde_json::to_string_pretty(&list) {
|
||||
Ok(data) => {
|
||||
if let Err(e) = fs::write(&self.storage_path, data) {
|
||||
tracing::error!("Failed to persist scheduler startup state: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => tracing::error!("Failed to serialize scheduler startup state: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
for job_to_load in list {
|
||||
if !Path::new(&job_to_load.source).exists() {
|
||||
tracing::warn!(
|
||||
|
|
@ -554,12 +578,15 @@ impl Scheduler {
|
|||
|
||||
pub async fn list_scheduled_jobs(&self) -> Vec<ScheduledJob> {
|
||||
self.sync_from_storage().await;
|
||||
self.jobs
|
||||
let mut jobs: Vec<ScheduledJob> = self
|
||||
.jobs
|
||||
.lock()
|
||||
.await
|
||||
.values()
|
||||
.map(|(_, j)| j.clone())
|
||||
.collect()
|
||||
.collect();
|
||||
jobs.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
jobs
|
||||
}
|
||||
|
||||
pub async fn remove_scheduled_job(
|
||||
|
|
@ -648,6 +675,7 @@ impl Scheduler {
|
|||
cancel_token.clone(),
|
||||
)
|
||||
.await;
|
||||
let was_cancelled = cancel_token.is_cancelled();
|
||||
|
||||
{
|
||||
let mut tasks = self.running_tasks.lock().await;
|
||||
|
|
@ -667,6 +695,10 @@ impl Scheduler {
|
|||
persist_jobs(&self.storage_path, &self.jobs).await?;
|
||||
|
||||
match result {
|
||||
_ if was_cancelled => Err(SchedulerError::AnyhowError(anyhow!(
|
||||
"Job '{}' was successfully cancelled",
|
||||
sched_id
|
||||
))),
|
||||
Ok(session_id) => Ok(session_id),
|
||||
Err(e) => Err(SchedulerError::AnyhowError(anyhow!(
|
||||
"Job '{}' failed: {}",
|
||||
|
|
@ -770,14 +802,25 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
let token = {
|
||||
let mut tasks = self.running_tasks.lock().await;
|
||||
tasks.remove(sched_id)
|
||||
};
|
||||
if let Some(token) = token {
|
||||
token.cancel();
|
||||
}
|
||||
|
||||
{
|
||||
let tasks = self.running_tasks.lock().await;
|
||||
if let Some(token) = tasks.get(sched_id) {
|
||||
token.cancel();
|
||||
let mut jobs_guard = self.jobs.lock().await;
|
||||
match jobs_guard.get_mut(sched_id) {
|
||||
Some((_, job)) => {
|
||||
clear_running_state(job);
|
||||
}
|
||||
None => return Err(SchedulerError::JobNotFound(sched_id.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
persist_jobs(&self.storage_path, &self.jobs).await
|
||||
}
|
||||
|
||||
pub async fn get_running_job_info(
|
||||
|
|
@ -1227,6 +1270,112 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_kill_running_job_clears_state_and_persists() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let storage_path = temp_dir.path().join("schedule.json");
|
||||
let recipe_path = create_test_recipe(temp_dir.path(), "running_job");
|
||||
let session_manager = Arc::new(SessionManager::new(temp_dir.path().to_path_buf()));
|
||||
let scheduler = Scheduler::new(storage_path.clone(), session_manager)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let job = ScheduledJob {
|
||||
id: "running_job".to_string(),
|
||||
source: recipe_path.to_string_lossy().to_string(),
|
||||
cron: "0 0 0 1 1 *".to_string(),
|
||||
last_run: None,
|
||||
currently_running: false,
|
||||
paused: false,
|
||||
current_session_id: None,
|
||||
process_start_time: None,
|
||||
parameters: vec![],
|
||||
recipe_base_dir: None,
|
||||
};
|
||||
|
||||
scheduler.add_scheduled_job(job, false).await.unwrap();
|
||||
{
|
||||
let mut jobs_guard = scheduler.jobs.lock().await;
|
||||
let (_, job) = jobs_guard.get_mut("running_job").unwrap();
|
||||
job.currently_running = true;
|
||||
job.current_session_id = Some("session-id".to_string());
|
||||
job.process_start_time = Some(Utc::now());
|
||||
}
|
||||
{
|
||||
let mut tasks = scheduler.running_tasks.lock().await;
|
||||
tasks.insert("running_job".to_string(), CancellationToken::new());
|
||||
}
|
||||
persist_jobs(&storage_path, &scheduler.jobs).await.unwrap();
|
||||
|
||||
scheduler.kill_running_job("running_job").await.unwrap();
|
||||
|
||||
let jobs = scheduler.list_scheduled_jobs().await;
|
||||
let killed_job = jobs.iter().find(|job| job.id == "running_job").unwrap();
|
||||
assert!(!killed_job.currently_running);
|
||||
assert!(killed_job.current_session_id.is_none());
|
||||
assert!(killed_job.process_start_time.is_none());
|
||||
assert!(scheduler.running_tasks.lock().await.is_empty());
|
||||
|
||||
let persisted_jobs: Vec<ScheduledJob> =
|
||||
serde_json::from_str(&fs::read_to_string(storage_path).unwrap()).unwrap();
|
||||
let persisted_job = persisted_jobs
|
||||
.iter()
|
||||
.find(|job| job.id == "running_job")
|
||||
.unwrap();
|
||||
assert!(!persisted_job.currently_running);
|
||||
assert!(persisted_job.current_session_id.is_none());
|
||||
assert!(persisted_job.process_start_time.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_load_jobs_from_storage_clears_stale_running_state() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
let storage_path = temp_dir.path().join("schedule.json");
|
||||
let recipe_path = create_test_recipe(temp_dir.path(), "stale_running_job");
|
||||
let started_at = Utc::now();
|
||||
let stale_job = ScheduledJob {
|
||||
id: "stale_running_job".to_string(),
|
||||
source: recipe_path.to_string_lossy().to_string(),
|
||||
cron: "0 0 0 1 1 *".to_string(),
|
||||
last_run: None,
|
||||
currently_running: true,
|
||||
paused: false,
|
||||
current_session_id: Some("stale-session-id".to_string()),
|
||||
process_start_time: Some(started_at),
|
||||
parameters: vec![],
|
||||
recipe_base_dir: None,
|
||||
};
|
||||
fs::write(
|
||||
&storage_path,
|
||||
serde_json::to_string_pretty(&vec![stale_job]).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let session_manager = Arc::new(SessionManager::new(temp_dir.path().to_path_buf()));
|
||||
let scheduler = Scheduler::new(storage_path.clone(), session_manager)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let jobs = scheduler.list_scheduled_jobs().await;
|
||||
let loaded_job = jobs
|
||||
.iter()
|
||||
.find(|job| job.id == "stale_running_job")
|
||||
.unwrap();
|
||||
assert!(!loaded_job.currently_running);
|
||||
assert!(loaded_job.current_session_id.is_none());
|
||||
assert!(loaded_job.process_start_time.is_none());
|
||||
|
||||
let persisted_jobs: Vec<ScheduledJob> =
|
||||
serde_json::from_str(&fs::read_to_string(storage_path).unwrap()).unwrap();
|
||||
let persisted_job = persisted_jobs
|
||||
.iter()
|
||||
.find(|job| job.id == "stale_running_job")
|
||||
.unwrap();
|
||||
assert!(!persisted_job.currently_running);
|
||||
assert!(persisted_job.current_session_id.is_none());
|
||||
assert!(persisted_job.process_start_time.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_job_with_no_prompt_does_not_panic() {
|
||||
let _guard = env_lock::lock_env([
|
||||
|
|
|
|||
193
ui/desktop/src/acp/schedules.ts
Normal file
193
ui/desktop/src/acp/schedules.ts
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import type {
|
||||
CreateScheduleRequest_unstable,
|
||||
InspectRunningJobResponse_unstable,
|
||||
KillRunningJobResponse_unstable,
|
||||
RunScheduleNowResponse_unstable,
|
||||
ScheduledJobDto,
|
||||
SessionInfo,
|
||||
} from '@aaif/goose-sdk';
|
||||
import { getAcpClient } from './acpConnection';
|
||||
|
||||
let inFlightListSchedules: Promise<ScheduledJobDto[]> | null = null;
|
||||
const inFlightListScheduleSessions = new Map<string, Promise<SessionInfo[]>>();
|
||||
|
||||
function acpErrorMessage(error: unknown): string | null {
|
||||
if (typeof error !== 'object' || error === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const candidate = 'error' in error && isRecord(error.error) ? error.error : error;
|
||||
if (!isRecord(candidate)) {
|
||||
return null;
|
||||
}
|
||||
if (typeof candidate.data === 'string') {
|
||||
return candidate.data;
|
||||
}
|
||||
return typeof candidate.message === 'string' ? candidate.message : null;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
function normalizeAcpError(error: unknown, fallback: string): Error {
|
||||
const message = acpErrorMessage(error);
|
||||
if (message) {
|
||||
return new Error(message);
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return error;
|
||||
}
|
||||
return new Error(fallback);
|
||||
}
|
||||
|
||||
function clearInFlightScheduleReads(): void {
|
||||
inFlightListSchedules = null;
|
||||
inFlightListScheduleSessions.clear();
|
||||
}
|
||||
|
||||
export async function acpListSchedules(): Promise<ScheduledJobDto[]> {
|
||||
const pending = inFlightListSchedules;
|
||||
if (pending) {
|
||||
return pending;
|
||||
}
|
||||
|
||||
const listPromise = (async () => {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesList_unstable({});
|
||||
return response.jobs;
|
||||
})().catch((error) => {
|
||||
throw normalizeAcpError(error, 'Failed to list schedules');
|
||||
});
|
||||
|
||||
inFlightListSchedules = listPromise;
|
||||
|
||||
try {
|
||||
return await listPromise;
|
||||
} finally {
|
||||
if (inFlightListSchedules === listPromise) {
|
||||
inFlightListSchedules = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpCreateSchedule(
|
||||
request: CreateScheduleRequest_unstable
|
||||
): Promise<ScheduledJobDto> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesCreate_unstable(request);
|
||||
clearInFlightScheduleReads();
|
||||
return response.job;
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to create schedule');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpDeleteSchedule(scheduleId: string): Promise<void> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
await client.goose.schedulesDelete_unstable({ scheduleId });
|
||||
clearInFlightScheduleReads();
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to delete schedule');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpListScheduleSessions(
|
||||
scheduleId: string,
|
||||
limit: number
|
||||
): Promise<SessionInfo[]> {
|
||||
const key = `${scheduleId}:${limit}`;
|
||||
const pending = inFlightListScheduleSessions.get(key);
|
||||
if (pending) {
|
||||
return pending;
|
||||
}
|
||||
|
||||
const listPromise = (async () => {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesSessionsList_unstable({ scheduleId, limit });
|
||||
return response.sessions;
|
||||
})().catch((error) => {
|
||||
throw normalizeAcpError(error, 'Failed to list schedule sessions');
|
||||
});
|
||||
|
||||
inFlightListScheduleSessions.set(key, listPromise);
|
||||
|
||||
try {
|
||||
return await listPromise;
|
||||
} finally {
|
||||
if (inFlightListScheduleSessions.get(key) === listPromise) {
|
||||
inFlightListScheduleSessions.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpRunScheduleNow(
|
||||
scheduleId: string
|
||||
): Promise<RunScheduleNowResponse_unstable> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesRunNow_unstable({ scheduleId });
|
||||
clearInFlightScheduleReads();
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to run schedule now');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpPauseSchedule(scheduleId: string): Promise<void> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
await client.goose.schedulesPause_unstable({ scheduleId });
|
||||
clearInFlightScheduleReads();
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to pause schedule');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpUnpauseSchedule(scheduleId: string): Promise<void> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
await client.goose.schedulesUnpause_unstable({ scheduleId });
|
||||
clearInFlightScheduleReads();
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to unpause schedule');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpUpdateSchedule(
|
||||
scheduleId: string,
|
||||
cron: string
|
||||
): Promise<ScheduledJobDto> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesUpdate_unstable({ scheduleId, cron });
|
||||
clearInFlightScheduleReads();
|
||||
return response.job;
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to update schedule');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpKillRunningJob(jobId: string): Promise<KillRunningJobResponse_unstable> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.schedulesRunningJobKill_unstable({ jobId });
|
||||
clearInFlightScheduleReads();
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to kill running job');
|
||||
}
|
||||
}
|
||||
|
||||
export async function acpInspectRunningJob(
|
||||
jobId: string
|
||||
): Promise<InspectRunningJobResponse_unstable> {
|
||||
try {
|
||||
const client = await getAcpClient();
|
||||
return await client.goose.schedulesRunningJobInspect_unstable({ jobId });
|
||||
} catch (error) {
|
||||
throw normalizeAcpError(error, 'Failed to inspect running job');
|
||||
}
|
||||
}
|
||||
|
|
@ -991,8 +991,8 @@ export default function RecipesView() {
|
|||
id: scheduleRecipeManifest.id,
|
||||
source: '',
|
||||
cron: scheduleRecipeManifest.schedule_cron,
|
||||
last_run: null,
|
||||
currently_running: false,
|
||||
lastRun: null,
|
||||
currentlyRunning: false,
|
||||
paused: false,
|
||||
}
|
||||
: null
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { ScheduledJob } from '../../schedule';
|
||||
import type { ScheduledJobDto } from '@aaif/goose-sdk';
|
||||
import { errorMessage } from '../../utils/conversionUtils';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import {
|
||||
|
|
@ -61,7 +61,7 @@ const i18n = defineMessages({
|
|||
});
|
||||
|
||||
interface CronPickerProps {
|
||||
schedule: ScheduledJob | null;
|
||||
schedule: ScheduledJobDto | null;
|
||||
onChange: (cron: string) => void;
|
||||
isValid: (valid: boolean) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,28 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import type { ScheduledJobDto, SessionInfo } from '@aaif/goose-sdk';
|
||||
import { Button } from '../ui/button';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import BackButton from '../ui/BackButton';
|
||||
import { Card } from '../ui/card';
|
||||
import {
|
||||
getScheduleSessions,
|
||||
runScheduleNow,
|
||||
pauseSchedule,
|
||||
unpauseSchedule,
|
||||
updateSchedule,
|
||||
listSchedules,
|
||||
killRunningJob,
|
||||
inspectRunningJob,
|
||||
ScheduledJob,
|
||||
} from '../../schedule';
|
||||
import SessionHistoryView from '../sessions/SessionHistoryView';
|
||||
acpListScheduleSessions,
|
||||
acpRunScheduleNow,
|
||||
acpPauseSchedule,
|
||||
acpUnpauseSchedule,
|
||||
acpUpdateSchedule,
|
||||
acpListSchedules,
|
||||
acpKillRunningJob,
|
||||
acpInspectRunningJob,
|
||||
} from '../../acp/schedules';
|
||||
import { ScheduleModal, NewSchedulePayload } from './ScheduleModal';
|
||||
import { toastError, toastSuccess } from '../../toasts';
|
||||
import { Loader2, Pause, Play, Edit, Square, Eye } from 'lucide-react';
|
||||
import cronstrue from 'cronstrue';
|
||||
import { formatToLocalDateWithTimezone } from '../../utils/date';
|
||||
import { getSession, Session } from '../../api';
|
||||
import { trackScheduleRunNow, getErrorType } from '../../utils/analytics';
|
||||
import { errorMessage } from '../../utils/conversionUtils';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import { useNavigation } from '../../hooks/useNavigation';
|
||||
|
||||
const i18n = defineMessages({
|
||||
scheduleNotFound: { id: 'scheduleDetailView.scheduleNotFound', defaultMessage: 'Schedule Not Found' },
|
||||
|
|
@ -57,12 +56,11 @@ const i18n = defineMessages({
|
|||
created: { id: 'scheduleDetailView.created', defaultMessage: 'Created: {date}' },
|
||||
messages: { id: 'scheduleDetailView.messages', defaultMessage: 'Messages: {count}' },
|
||||
dir: { id: 'scheduleDetailView.dir', defaultMessage: 'Dir: {path}' },
|
||||
tokens: { id: 'scheduleDetailView.tokens', defaultMessage: 'Tokens: {count}' },
|
||||
idLabel: { id: 'scheduleDetailView.idLabel', defaultMessage: 'ID:' },
|
||||
jobCancelled: { id: 'scheduleDetailView.jobCancelled', defaultMessage: 'Job Cancelled' },
|
||||
jobCancelledMsg: { id: 'scheduleDetailView.jobCancelledMsg', defaultMessage: 'The job was cancelled while starting up.' },
|
||||
scheduleTriggered: { id: 'scheduleDetailView.scheduleTriggered', defaultMessage: 'Schedule Triggered' },
|
||||
newSession: { id: 'scheduleDetailView.newSession', defaultMessage: 'New session: {sessionId}' },
|
||||
scheduleCompleted: { id: 'scheduleDetailView.scheduleCompleted', defaultMessage: 'Run completed' },
|
||||
completedSession: { id: 'scheduleDetailView.completedSession', defaultMessage: 'Session: {sessionId}' },
|
||||
runScheduleError: { id: 'scheduleDetailView.runScheduleError', defaultMessage: 'Run Schedule Error' },
|
||||
scheduleUnpaused: { id: 'scheduleDetailView.scheduleUnpaused', defaultMessage: 'Schedule Unpaused' },
|
||||
unpausedMsg: { id: 'scheduleDetailView.unpausedMsg', defaultMessage: 'Unpaused "{id}"' },
|
||||
|
|
@ -77,53 +75,48 @@ const i18n = defineMessages({
|
|||
scheduleUpdated: { id: 'scheduleDetailView.scheduleUpdated', defaultMessage: 'Schedule Updated' },
|
||||
updatedMsg: { id: 'scheduleDetailView.updatedMsg', defaultMessage: 'Updated "{id}"' },
|
||||
updateScheduleError: { id: 'scheduleDetailView.updateScheduleError', defaultMessage: 'Update Schedule Error' },
|
||||
failedToLoadSession: { id: 'scheduleDetailView.failedToLoadSession', defaultMessage: 'Failed to load session' },
|
||||
scheduleNotFoundError: { id: 'scheduleDetailView.scheduleNotFoundError', defaultMessage: 'Schedule not found' },
|
||||
});
|
||||
|
||||
interface ScheduleSessionMeta {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
workingDir?: string;
|
||||
scheduleId?: string | null;
|
||||
messageCount?: number;
|
||||
totalTokens?: number | null;
|
||||
inputTokens?: number | null;
|
||||
outputTokens?: number | null;
|
||||
accumulatedTotalTokens?: number | null;
|
||||
accumulatedInputTokens?: number | null;
|
||||
accumulatedOutputTokens?: number | null;
|
||||
}
|
||||
|
||||
interface ScheduleDetailViewProps {
|
||||
scheduleId: string | null;
|
||||
onNavigateBack: () => void;
|
||||
}
|
||||
|
||||
function sessionMeta(session: SessionInfo): Record<string, unknown> {
|
||||
return typeof session._meta === 'object' && session._meta !== null ? session._meta : {};
|
||||
}
|
||||
|
||||
function metaString(session: SessionInfo, key: string): string | undefined {
|
||||
const value = sessionMeta(session)[key];
|
||||
return typeof value === 'string' ? value : undefined;
|
||||
}
|
||||
|
||||
function metaNumber(session: SessionInfo, key: string): number | undefined {
|
||||
const value = sessionMeta(session)[key];
|
||||
return typeof value === 'number' ? value : undefined;
|
||||
}
|
||||
|
||||
const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onNavigateBack }) => {
|
||||
const intl = useIntl();
|
||||
const [sessions, setSessions] = useState<ScheduleSessionMeta[]>([]);
|
||||
const setView = useNavigation();
|
||||
const [sessions, setSessions] = useState<SessionInfo[]>([]);
|
||||
const [isLoadingSessions, setIsLoadingSessions] = useState(false);
|
||||
const [sessionsError, setSessionsError] = useState<string | null>(null);
|
||||
|
||||
const [scheduleDetails, setScheduleDetails] = useState<ScheduledJob | null>(null);
|
||||
const [scheduleDetails, setScheduleDetails] = useState<ScheduledJobDto | null>(null);
|
||||
const [isLoadingSchedule, setIsLoadingSchedule] = useState(false);
|
||||
const [scheduleError, setScheduleError] = useState<string | null>(null);
|
||||
|
||||
const [isActionLoading, setIsActionLoading] = useState(false);
|
||||
|
||||
const [selectedSession, setSelectedSession] = useState<Session | null>(null);
|
||||
const [isLoadingSession, setIsLoadingSession] = useState(false);
|
||||
const [sessionError, setSessionError] = useState<string | null>(null);
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const fetchSessions = async (sId: string) => {
|
||||
setIsLoadingSessions(true);
|
||||
setSessionsError(null);
|
||||
try {
|
||||
const data = await getScheduleSessions(sId, 20);
|
||||
const data = await acpListScheduleSessions(sId, 20);
|
||||
setSessions(data);
|
||||
} catch (err) {
|
||||
setSessionsError(errorMessage(err, 'Failed to fetch sessions'));
|
||||
|
|
@ -136,7 +129,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
setIsLoadingSchedule(true);
|
||||
setScheduleError(null);
|
||||
try {
|
||||
const allSchedules = await listSchedules();
|
||||
const allSchedules = await acpListSchedules();
|
||||
const schedule = allSchedules.find((s) => s.id === sId);
|
||||
if (schedule) {
|
||||
setScheduleDetails(schedule);
|
||||
|
|
@ -151,22 +144,27 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
}, [intl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (scheduleId && !selectedSession) {
|
||||
if (scheduleId) {
|
||||
fetchSessions(scheduleId);
|
||||
fetchSchedule(scheduleId);
|
||||
}
|
||||
}, [scheduleId, selectedSession, fetchSchedule]);
|
||||
}, [scheduleId, fetchSchedule]);
|
||||
|
||||
const openSession = useCallback((sessionId: string) => {
|
||||
setView('pair', {
|
||||
disableAnimation: true,
|
||||
resumeSessionId: sessionId,
|
||||
});
|
||||
}, [setView]);
|
||||
|
||||
const handleRunNow = async () => {
|
||||
if (!scheduleId) return;
|
||||
setIsActionLoading(true);
|
||||
try {
|
||||
const newSessionId = await runScheduleNow(scheduleId);
|
||||
const result = await acpRunScheduleNow(scheduleId);
|
||||
trackScheduleRunNow(true);
|
||||
if (newSessionId === 'CANCELLED') {
|
||||
toastSuccess({ title: intl.formatMessage(i18n.jobCancelled), msg: intl.formatMessage(i18n.jobCancelledMsg) });
|
||||
} else {
|
||||
toastSuccess({ title: intl.formatMessage(i18n.scheduleTriggered), msg: intl.formatMessage(i18n.newSession, { sessionId: newSessionId }) });
|
||||
if (result.status === 'completed' && result.sessionId) {
|
||||
toastSuccess({ title: intl.formatMessage(i18n.scheduleCompleted), msg: intl.formatMessage(i18n.completedSession, { sessionId: result.sessionId }) });
|
||||
}
|
||||
await fetchSessions(scheduleId);
|
||||
await fetchSchedule(scheduleId);
|
||||
|
|
@ -187,10 +185,10 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
setIsActionLoading(true);
|
||||
try {
|
||||
if (scheduleDetails.paused) {
|
||||
await unpauseSchedule(scheduleId);
|
||||
await acpUnpauseSchedule(scheduleId);
|
||||
toastSuccess({ title: intl.formatMessage(i18n.scheduleUnpaused), msg: intl.formatMessage(i18n.unpausedMsg, { id: scheduleId }) });
|
||||
} else {
|
||||
await pauseSchedule(scheduleId);
|
||||
await acpPauseSchedule(scheduleId);
|
||||
toastSuccess({ title: intl.formatMessage(i18n.schedulePaused), msg: intl.formatMessage(i18n.pausedMsg, { id: scheduleId }) });
|
||||
}
|
||||
await fetchSchedule(scheduleId);
|
||||
|
|
@ -209,7 +207,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
if (!scheduleId) return;
|
||||
setIsActionLoading(true);
|
||||
try {
|
||||
const result = await killRunningJob(scheduleId);
|
||||
const result = await acpKillRunningJob(scheduleId);
|
||||
toastSuccess({ title: intl.formatMessage(i18n.jobKilled), msg: result.message });
|
||||
await fetchSchedule(scheduleId);
|
||||
} catch (err) {
|
||||
|
|
@ -227,7 +225,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
if (!scheduleId) return;
|
||||
setIsActionLoading(true);
|
||||
try {
|
||||
const result = await inspectRunningJob(scheduleId);
|
||||
const result = await acpInspectRunningJob(scheduleId);
|
||||
if (result.sessionId) {
|
||||
const duration = result.runningDurationSeconds
|
||||
? `${Math.floor(result.runningDurationSeconds / 60)}m ${result.runningDurationSeconds % 60}s`
|
||||
|
|
@ -254,7 +252,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
if (!scheduleId) return;
|
||||
setIsActionLoading(true);
|
||||
try {
|
||||
await updateSchedule(scheduleId, payload as string);
|
||||
await acpUpdateSchedule(scheduleId, payload as string);
|
||||
toastSuccess({ title: intl.formatMessage(i18n.scheduleUpdated), msg: intl.formatMessage(i18n.updatedMsg, { id: scheduleId }) });
|
||||
await fetchSchedule(scheduleId);
|
||||
setIsModalOpen(false);
|
||||
|
|
@ -269,37 +267,6 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
}
|
||||
};
|
||||
|
||||
const loadSession = async (sessionId: string) => {
|
||||
setIsLoadingSession(true);
|
||||
setSessionError(null);
|
||||
try {
|
||||
const response = await getSession<true>({
|
||||
path: { session_id: sessionId },
|
||||
throwOnError: true,
|
||||
});
|
||||
setSelectedSession(response.data);
|
||||
} catch (err) {
|
||||
const msg = errorMessage(err, 'Failed to load session');
|
||||
setSessionError(msg);
|
||||
toastError({ title: intl.formatMessage(i18n.failedToLoadSession), msg });
|
||||
} finally {
|
||||
setIsLoadingSession(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedSession) {
|
||||
return (
|
||||
<SessionHistoryView
|
||||
session={selectedSession}
|
||||
isLoading={isLoadingSession}
|
||||
error={sessionError}
|
||||
onBack={() => setSelectedSession(null)}
|
||||
onRetry={() => loadSession(selectedSession.id)}
|
||||
showActionButtons={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!scheduleId) {
|
||||
return (
|
||||
<div className="h-screen w-full flex flex-col items-center justify-center bg-white dark:bg-gray-900 text-text-primary p-8">
|
||||
|
|
@ -352,7 +319,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
{scheduleDetails.id}
|
||||
</h3>
|
||||
<div className="mt-2 md:mt-0 flex items-center gap-2">
|
||||
{scheduleDetails.currently_running && (
|
||||
{scheduleDetails.currentlyRunning && (
|
||||
<div className="text-sm text-green-500 dark:text-green-400 font-semibold flex items-center">
|
||||
<span className="inline-block w-2 h-2 bg-green-500 dark:bg-green-400 rounded-full mr-1 animate-pulse"></span>
|
||||
{intl.formatMessage(i18n.currentlyRunning)}
|
||||
|
|
@ -377,18 +344,18 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
</p>
|
||||
<p className="text-sm text-text-primary">
|
||||
<span className="font-semibold">{intl.formatMessage(i18n.lastRun)}</span>{' '}
|
||||
{formatToLocalDateWithTimezone(scheduleDetails.last_run)}
|
||||
{formatToLocalDateWithTimezone(scheduleDetails.lastRun)}
|
||||
</p>
|
||||
{scheduleDetails.currently_running && scheduleDetails.current_session_id && (
|
||||
{scheduleDetails.currentlyRunning && scheduleDetails.currentSessionId && (
|
||||
<p className="text-sm text-text-primary">
|
||||
<span className="font-semibold">{intl.formatMessage(i18n.currentSession)}</span>{' '}
|
||||
{scheduleDetails.current_session_id}
|
||||
{scheduleDetails.currentSessionId}
|
||||
</p>
|
||||
)}
|
||||
{scheduleDetails.currently_running && scheduleDetails.process_start_time && (
|
||||
{scheduleDetails.currentlyRunning && scheduleDetails.jobStartTime && (
|
||||
<p className="text-sm text-text-primary">
|
||||
<span className="font-semibold">{intl.formatMessage(i18n.processStarted)}</span>{' '}
|
||||
{formatToLocalDateWithTimezone(scheduleDetails.process_start_time)}
|
||||
{formatToLocalDateWithTimezone(scheduleDetails.jobStartTime)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -401,13 +368,13 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
<div className="flex flex-col md:flex-row gap-2">
|
||||
<Button
|
||||
onClick={handleRunNow}
|
||||
disabled={isActionLoading || scheduleDetails?.currently_running}
|
||||
disabled={isActionLoading || scheduleDetails?.currentlyRunning}
|
||||
className="w-full md:w-auto"
|
||||
>
|
||||
{intl.formatMessage(i18n.runScheduleNow)}
|
||||
</Button>
|
||||
|
||||
{scheduleDetails && !scheduleDetails.currently_running && (
|
||||
{scheduleDetails && !scheduleDetails.currentlyRunning && (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
|
|
@ -443,7 +410,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
</>
|
||||
)}
|
||||
|
||||
{scheduleDetails?.currently_running && (
|
||||
{scheduleDetails?.currentlyRunning && (
|
||||
<>
|
||||
<Button
|
||||
onClick={handleInspect}
|
||||
|
|
@ -467,7 +434,7 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
)}
|
||||
</div>
|
||||
|
||||
{scheduleDetails?.currently_running && (
|
||||
{scheduleDetails?.currentlyRunning && (
|
||||
<p className="text-sm text-amber-600 dark:text-amber-400 mt-2">
|
||||
{intl.formatMessage(i18n.cannotModifyRunning)}
|
||||
</p>
|
||||
|
|
@ -496,45 +463,46 @@ const ScheduleDetailView: React.FC<ScheduleDetailViewProps> = ({ scheduleId, onN
|
|||
|
||||
{sessions.length > 0 && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{sessions.map((session) => (
|
||||
<Card
|
||||
key={session.id}
|
||||
className="p-4 bg-background-primary shadow cursor-pointer hover:shadow-lg transition-shadow duration-200"
|
||||
onClick={() => loadSession(session.id)}
|
||||
>
|
||||
<h3
|
||||
className="text-sm font-semibold text-text-primary truncate"
|
||||
title={session.name || session.id}
|
||||
{sessions.map((session) => {
|
||||
const sessionId = String(session.sessionId);
|
||||
const sessionName = session.title ?? sessionId;
|
||||
const createdAt = metaString(session, 'createdAt') ?? session.updatedAt;
|
||||
const messageCount = metaNumber(session, 'messageCount');
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={sessionId}
|
||||
className="p-4 bg-background-primary shadow cursor-pointer hover:shadow-lg transition-shadow duration-200"
|
||||
onClick={() => openSession(sessionId)}
|
||||
>
|
||||
{session.name || intl.formatMessage(i18n.sessionId, { id: session.id })}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.created, { date: session.createdAt ? formatToLocalDateWithTimezone(session.createdAt) : 'N/A' })}
|
||||
</p>
|
||||
{session.messageCount !== undefined && (
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.messages, { count: session.messageCount })}
|
||||
</p>
|
||||
)}
|
||||
{session.workingDir && (
|
||||
<p
|
||||
className="text-xs text-text-secondary mt-1 truncate"
|
||||
title={session.workingDir}
|
||||
<h3
|
||||
className="text-sm font-semibold text-text-primary truncate"
|
||||
title={sessionName}
|
||||
>
|
||||
{intl.formatMessage(i18n.dir, { path: session.workingDir })}
|
||||
{sessionName || intl.formatMessage(i18n.sessionId, { id: sessionId })}
|
||||
</h3>
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.created, { date: createdAt ? formatToLocalDateWithTimezone(createdAt) : 'N/A' })}
|
||||
</p>
|
||||
)}
|
||||
{session.accumulatedTotalTokens !== undefined &&
|
||||
session.accumulatedTotalTokens !== null && (
|
||||
{messageCount !== undefined && (
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.tokens, { count: session.accumulatedTotalTokens })}
|
||||
{intl.formatMessage(i18n.messages, { count: messageCount })}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.idLabel)} <span className="font-mono">{session.id}</span>
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
{session.cwd && (
|
||||
<p
|
||||
className="text-xs text-text-secondary mt-1 truncate"
|
||||
title={session.cwd}
|
||||
>
|
||||
{intl.formatMessage(i18n.dir, { path: session.cwd })}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-text-secondary mt-1">
|
||||
{intl.formatMessage(i18n.idLabel)} <span className="font-mono">{sessionId}</span>
|
||||
</p>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useState, useEffect, FormEvent, useCallback } from 'react';
|
||||
import type { ScheduledJobDto } from '@aaif/goose-sdk';
|
||||
import { Card } from '../ui/card';
|
||||
import { Button } from '../ui/button';
|
||||
import { Input } from '../ui/input';
|
||||
import { ScheduledJob } from '../../schedule';
|
||||
import { CronPicker } from './CronPicker';
|
||||
import { Recipe, parseDeeplink, parseRecipeFromFile } from '../../recipe';
|
||||
import { getStorageDirectory } from '../../recipe/recipe_management';
|
||||
|
|
@ -47,7 +47,7 @@ interface ScheduleModalProps {
|
|||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (payload: NewSchedulePayload | string) => Promise<void>;
|
||||
schedule: ScheduledJob | null;
|
||||
schedule: ScheduledJobDto | null;
|
||||
isLoadingExternally: boolean;
|
||||
apiErrorExternally: string | null;
|
||||
initialDeepLink: string | null;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import type { ScheduledJobDto } from '@aaif/goose-sdk';
|
||||
import {
|
||||
listSchedules,
|
||||
createSchedule,
|
||||
deleteSchedule,
|
||||
pauseSchedule,
|
||||
unpauseSchedule,
|
||||
updateSchedule,
|
||||
killRunningJob,
|
||||
inspectRunningJob,
|
||||
ScheduledJob,
|
||||
} from '../../schedule';
|
||||
acpListSchedules,
|
||||
acpCreateSchedule,
|
||||
acpDeleteSchedule,
|
||||
acpPauseSchedule,
|
||||
acpUnpauseSchedule,
|
||||
acpUpdateSchedule,
|
||||
acpKillRunningJob,
|
||||
acpInspectRunningJob,
|
||||
} from '../../acp/schedules';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { Card } from '../ui/card';
|
||||
import { Button } from '../ui/button';
|
||||
|
|
@ -67,9 +67,9 @@ interface SchedulesViewProps {
|
|||
}
|
||||
|
||||
const ScheduleCard: React.FC<{
|
||||
job: ScheduledJob;
|
||||
job: ScheduledJobDto;
|
||||
onNavigateToDetail: (id: string) => void;
|
||||
onEdit: (job: ScheduledJob) => void;
|
||||
onEdit: (job: ScheduledJobDto) => void;
|
||||
onPause: (id: string) => void;
|
||||
onUnpause: (id: string) => void;
|
||||
onKill: (id: string) => void;
|
||||
|
|
@ -95,7 +95,7 @@ const ScheduleCard: React.FC<{
|
|||
readableCron = job.cron;
|
||||
}
|
||||
|
||||
const formattedLastRun = formatToLocalDateWithTimezone(job.last_run);
|
||||
const formattedLastRun = formatToLocalDateWithTimezone(job.lastRun);
|
||||
|
||||
return (
|
||||
<Card
|
||||
|
|
@ -108,7 +108,7 @@ const ScheduleCard: React.FC<{
|
|||
<h3 className="text-base truncate max-w-[50vw]" title={job.id}>
|
||||
{job.id}
|
||||
</h3>
|
||||
{job.currently_running && (
|
||||
{job.currentlyRunning && (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">
|
||||
<span className="inline-block w-2 h-2 bg-green-500 rounded-full mr-1 animate-pulse"></span>
|
||||
{intl.formatMessage(i18n.running)}
|
||||
|
|
@ -130,7 +130,7 @@ const ScheduleCard: React.FC<{
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
{!job.currently_running && (
|
||||
{!job.currentlyRunning && (
|
||||
<>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
|
|
@ -173,7 +173,7 @@ const ScheduleCard: React.FC<{
|
|||
</Button>
|
||||
</>
|
||||
)}
|
||||
{job.currently_running && (
|
||||
{job.currentlyRunning && (
|
||||
<>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
|
|
@ -224,13 +224,13 @@ const ScheduleCard: React.FC<{
|
|||
const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
||||
const intl = useIntl();
|
||||
const location = useLocation();
|
||||
const [schedules, setSchedules] = useState<ScheduledJob[]>([]);
|
||||
const [schedules, setSchedules] = useState<ScheduledJobDto[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [apiError, setApiError] = useState<string | null>(null);
|
||||
const [submitApiError, setSubmitApiError] = useState<string | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [editingSchedule, setEditingSchedule] = useState<ScheduledJob | null>(null);
|
||||
const [editingSchedule, setEditingSchedule] = useState<ScheduledJobDto | null>(null);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [pendingDeepLink, setPendingDeepLink] = useState<string | null>(null);
|
||||
const [actionsInProgress, setActionsInProgress] = useState<Set<string>>(new Set());
|
||||
|
|
@ -240,7 +240,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setIsLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
const fetchedSchedules = await listSchedules();
|
||||
const fetchedSchedules = await acpListSchedules();
|
||||
setSchedules(fetchedSchedules);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch schedules:', error);
|
||||
|
|
@ -289,14 +289,14 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setSubmitApiError(null);
|
||||
try {
|
||||
if (editingSchedule) {
|
||||
await updateSchedule(editingSchedule.id, payload as string);
|
||||
await acpUpdateSchedule(editingSchedule.id, payload as string);
|
||||
toastSuccess({
|
||||
title: intl.formatMessage(i18n.scheduleUpdated),
|
||||
msg: intl.formatMessage(i18n.scheduleUpdatedMsg, { id: editingSchedule.id }),
|
||||
});
|
||||
} else {
|
||||
const newPayload = payload as NewSchedulePayload;
|
||||
await createSchedule(newPayload);
|
||||
await acpCreateSchedule(newPayload);
|
||||
const sourceType = pendingDeepLink ? 'deeplink' : 'file';
|
||||
trackScheduleCreated(sourceType, true);
|
||||
}
|
||||
|
|
@ -325,7 +325,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setApiError(null);
|
||||
|
||||
try {
|
||||
await deleteSchedule(id);
|
||||
await acpDeleteSchedule(id);
|
||||
trackScheduleDeleted(true);
|
||||
await fetchSchedules();
|
||||
} catch (error) {
|
||||
|
|
@ -347,7 +347,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setApiError(null);
|
||||
|
||||
try {
|
||||
await pauseSchedule(id);
|
||||
await acpPauseSchedule(id);
|
||||
toastSuccess({
|
||||
title: intl.formatMessage(i18n.schedulePaused),
|
||||
msg: intl.formatMessage(i18n.schedulePausedMsg, { id }),
|
||||
|
|
@ -375,7 +375,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setApiError(null);
|
||||
|
||||
try {
|
||||
await unpauseSchedule(id);
|
||||
await acpUnpauseSchedule(id);
|
||||
toastSuccess({
|
||||
title: intl.formatMessage(i18n.scheduleUnpaused),
|
||||
msg: intl.formatMessage(i18n.scheduleUnpausedMsg, { id }),
|
||||
|
|
@ -403,7 +403,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setApiError(null);
|
||||
|
||||
try {
|
||||
const result = await killRunningJob(id);
|
||||
const result = await acpKillRunningJob(id);
|
||||
toastSuccess({
|
||||
title: intl.formatMessage(i18n.jobKilled),
|
||||
msg: result.message,
|
||||
|
|
@ -431,7 +431,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
|
|||
setApiError(null);
|
||||
|
||||
try {
|
||||
const result = await inspectRunningJob(id);
|
||||
const result = await acpInspectRunningJob(id);
|
||||
if (result.sessionId) {
|
||||
const duration = result.runningDurationSeconds
|
||||
? `${Math.floor(result.runningDurationSeconds / 60)}m ${result.runningDurationSeconds % 60}s`
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { render, screen, type RenderOptions, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { ScheduledJobDto } from '@aaif/goose-sdk';
|
||||
import { IntlTestWrapper } from '../../../i18n/test-utils';
|
||||
import { CronPicker } from '../CronPicker';
|
||||
import type { ScheduledJob } from '../../../schedule';
|
||||
|
||||
const renderWithIntl = (ui: React.ReactElement, options?: RenderOptions) =>
|
||||
render(ui, { wrapper: IntlTestWrapper, ...options });
|
||||
|
|
@ -13,10 +13,12 @@ const getLastCron = (onChange: ReturnType<typeof vi.fn>) => {
|
|||
return calls[calls.length - 1]?.[0];
|
||||
};
|
||||
|
||||
const scheduledJob = (cron: string): ScheduledJob => ({
|
||||
const scheduledJob = (cron: string): ScheduledJobDto => ({
|
||||
id: 'quarterly-report',
|
||||
source: 'dummy.yaml',
|
||||
cron,
|
||||
currentlyRunning: false,
|
||||
paused: false,
|
||||
});
|
||||
|
||||
describe('CronPicker', () => {
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "Cannot trigger or modify a schedule while it's already running."
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "Session: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "Created: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "Error: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "Failed to load session"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "Messages: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "New session: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "No schedule ID provided. Return to schedules list."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "Run Schedule Now"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "Run completed"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "Schedule Details"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "Schedule Paused"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "Schedule Triggered"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "Schedule Unpaused"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "Session ID: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "Tokens: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "Unpause Schedule"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "No se puede activar ni modificar una programación mientras se está ejecutando."
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "Sesión: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "Creada: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "Error: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "No se pudo cargar la sesión"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "Mensajes: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "Nueva sesión: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "No se proporcionó ningún ID de programación. Vuelve a la lista de programaciones."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "Ejecutar programación ahora"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "Ejecución completada"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "Detalles de la programación"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "Programación en pausa"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "Programación activada"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "Programación reanudada"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "ID de sesión: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "Tokens: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "Reanudar programación"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "जब कोई शेड्यूल पहले से चल रहा हो तो उसे ट्रिगर या संशोधित नहीं किया जा सकता।"
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "सत्र: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "बनाया गया: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "त्रुटि: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "सत्र लोड करने में विफल"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "आईडी:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "संदेश: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "नया सत्र: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "कोई शेड्यूल आईडी प्रदान नहीं की गई. शेड्यूल सूची पर लौटें."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "अभी शेड्यूल चलाएँ"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "रन पूरा हुआ"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "अनुसूची विवरण"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "शेड्यूल रोका गया"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "शेड्यूल ट्रिगर हुआ"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "शेड्यूल अनरोका"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "सत्र आईडी: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "टोकन: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "शेड्यूल को फिर से रोकें"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "既に実行中のため、スケジュールを実行または変更できません。"
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "セッション: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "作成: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "エラー: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "セッションの読み込みに失敗しました"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "メッセージ: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "新しいセッション: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "スケジュールIDが指定されていません。スケジュール一覧に戻ってください。"
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "スケジュールを今すぐ実行"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "実行が完了しました"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "スケジュール詳細"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "スケジュールを一時停止しました"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "スケジュールを実行しました"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "スケジュールを再開しました"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "セッションID: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "トークン: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "スケジュールを再開"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "이미 실행 중인 일정은 트리거하거나 수정할 수 없습니다."
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "세션: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "생성일: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "오류: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "세션을 로드하지 못했습니다."
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "메시지: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "새 세션: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "제공된 일정 ID가 없습니다. 일정 목록으로 돌아갑니다."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "지금 일정 실행"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "실행 완료"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "일정 세부정보"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "일정이 일시 중지됨"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "일정이 실행됨"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "일정 일시중지 해제됨"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "세션 ID: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "토큰: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "일정 일시중지 해제"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "Нельзя запускать или изменять расписание, пока оно выполняется."
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "Сеанс: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "Создано: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "Ошибка: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "Не удалось загрузить сеанс"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "Сообщений: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "Новый сеанс: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "ID расписания не указан. Вернитесь к списку расписаний."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "Запустить расписание сейчас"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "Запуск завершен"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "Детали расписания"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "Расписание приостановлено"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "Расписание запущено"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "Расписание возобновлено"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "ID сеанса: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "Токенов: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "Возобновить расписание"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "Zaten çalışmakta olan bir program tetiklenemez veya değiştirilemez."
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "Oturum: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "Oluşturuldu: {date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "Hata: {error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "Oturum yüklenemedi"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "Kimlik:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "Mesajlar: {count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "Yeni oturum: {sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "Program kimliği sağlanmadı. Program listesine geri dönün."
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "Şimdi Planla'yı Çalıştır"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "Çalıştırma tamamlandı"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "Program Ayrıntıları"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "Plan Duraklatıldı"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "Zamanlama Tetiklendi"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "Duraklatmayı Kaldırmayı Planla"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "Oturum Kimliği: {id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "Jetonlar: {count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "Programı Duraklatmayı Kaldır"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3296,6 +3296,9 @@
|
|||
"scheduleDetailView.cannotModifyRunning": {
|
||||
"defaultMessage": "计划正在运行中,无法触发或修改。"
|
||||
},
|
||||
"scheduleDetailView.completedSession": {
|
||||
"defaultMessage": "会话:{sessionId}"
|
||||
},
|
||||
"scheduleDetailView.created": {
|
||||
"defaultMessage": "创建于:{date}"
|
||||
},
|
||||
|
|
@ -3317,9 +3320,6 @@
|
|||
"scheduleDetailView.errorPrefix": {
|
||||
"defaultMessage": "错误:{error}"
|
||||
},
|
||||
"scheduleDetailView.failedToLoadSession": {
|
||||
"defaultMessage": "加载会话失败"
|
||||
},
|
||||
"scheduleDetailView.idLabel": {
|
||||
"defaultMessage": "ID:"
|
||||
},
|
||||
|
|
@ -3362,9 +3362,6 @@
|
|||
"scheduleDetailView.messages": {
|
||||
"defaultMessage": "消息:{count}"
|
||||
},
|
||||
"scheduleDetailView.newSession": {
|
||||
"defaultMessage": "新会话:{sessionId}"
|
||||
},
|
||||
"scheduleDetailView.noScheduleId": {
|
||||
"defaultMessage": "未提供计划 ID。返回计划列表。"
|
||||
},
|
||||
|
|
@ -3401,6 +3398,9 @@
|
|||
"scheduleDetailView.runScheduleNow": {
|
||||
"defaultMessage": "立即运行计划"
|
||||
},
|
||||
"scheduleDetailView.scheduleCompleted": {
|
||||
"defaultMessage": "运行已完成"
|
||||
},
|
||||
"scheduleDetailView.scheduleDetails": {
|
||||
"defaultMessage": "计划详情"
|
||||
},
|
||||
|
|
@ -3419,9 +3419,6 @@
|
|||
"scheduleDetailView.schedulePaused": {
|
||||
"defaultMessage": "计划已暂停"
|
||||
},
|
||||
"scheduleDetailView.scheduleTriggered": {
|
||||
"defaultMessage": "计划已触发"
|
||||
},
|
||||
"scheduleDetailView.scheduleUnpaused": {
|
||||
"defaultMessage": "计划已恢复"
|
||||
},
|
||||
|
|
@ -3431,9 +3428,6 @@
|
|||
"scheduleDetailView.sessionId": {
|
||||
"defaultMessage": "会话 ID:{id}"
|
||||
},
|
||||
"scheduleDetailView.tokens": {
|
||||
"defaultMessage": "令牌:{count}"
|
||||
},
|
||||
"scheduleDetailView.unpauseSchedule": {
|
||||
"defaultMessage": "恢复计划"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,191 +0,0 @@
|
|||
import {
|
||||
listSchedules as apiListSchedules,
|
||||
createSchedule as apiCreateSchedule,
|
||||
deleteSchedule as apiDeleteSchedule,
|
||||
pauseSchedule as apiPauseSchedule,
|
||||
unpauseSchedule as apiUnpauseSchedule,
|
||||
updateSchedule as apiUpdateSchedule,
|
||||
sessionsHandler as apiGetScheduleSessions,
|
||||
runNowHandler as apiRunScheduleNow,
|
||||
killRunningJob as apiKillRunningJob,
|
||||
inspectRunningJob as apiInspectRunningJob,
|
||||
SessionDisplayInfo,
|
||||
} from './api';
|
||||
import type { Recipe } from './recipe';
|
||||
|
||||
export interface ScheduledJob {
|
||||
id: string;
|
||||
source: string;
|
||||
cron: string;
|
||||
last_run?: string | null;
|
||||
currently_running?: boolean;
|
||||
paused?: boolean;
|
||||
current_session_id?: string | null;
|
||||
process_start_time?: string | null;
|
||||
}
|
||||
|
||||
export interface ScheduleSession {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: string; // ISO 8601 date string
|
||||
workingDir: string;
|
||||
scheduleId: string;
|
||||
messageCount: number;
|
||||
totalTokens: number;
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
accumulatedTotalTokens: number;
|
||||
accumulatedInputTokens: number;
|
||||
accumulatedOutputTokens: number;
|
||||
}
|
||||
|
||||
export async function listSchedules(): Promise<ScheduledJob[]> {
|
||||
try {
|
||||
const response = await apiListSchedules<true>();
|
||||
if (response && response.data && Array.isArray(response.data.jobs)) {
|
||||
return response.data.jobs as ScheduledJob[];
|
||||
}
|
||||
console.error('Unexpected response format from apiListSchedules', response);
|
||||
throw new Error('Failed to list schedules: Unexpected response format');
|
||||
} catch (error) {
|
||||
console.error('Error listing schedules:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSchedule(request: {
|
||||
id: string;
|
||||
recipe: Recipe;
|
||||
cron: string;
|
||||
}): Promise<ScheduledJob> {
|
||||
type ApiCreateScheduleBody = Parameters<typeof apiCreateSchedule>[0]['body'];
|
||||
const response = await apiCreateSchedule({ body: request as unknown as ApiCreateScheduleBody });
|
||||
if (response.data) {
|
||||
return response.data as ScheduledJob;
|
||||
}
|
||||
const err = response.error as { message?: string } | undefined;
|
||||
throw new Error(err?.message || 'Failed to create schedule');
|
||||
}
|
||||
|
||||
export async function deleteSchedule(id: string): Promise<void> {
|
||||
try {
|
||||
await apiDeleteSchedule<true>({ path: { id } });
|
||||
} catch (error) {
|
||||
console.error(`Error deleting schedule ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getScheduleSessions(
|
||||
scheduleId: string,
|
||||
limit: number
|
||||
): Promise<Array<SessionDisplayInfo>> {
|
||||
const response = await apiGetScheduleSessions<true>({
|
||||
path: { id: scheduleId },
|
||||
query: { limit },
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function runScheduleNow(scheduleId: string): Promise<string> {
|
||||
try {
|
||||
const response = await apiRunScheduleNow<true>({
|
||||
path: { id: scheduleId },
|
||||
});
|
||||
|
||||
if (response && response.data && response.data.session_id) {
|
||||
return response.data.session_id;
|
||||
}
|
||||
console.error('Unexpected response format from apiRunScheduleNow', response);
|
||||
throw new Error('Failed to run schedule now: Unexpected response format');
|
||||
} catch (error) {
|
||||
console.error(`Error running schedule ${scheduleId} now:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function pauseSchedule(scheduleId: string): Promise<void> {
|
||||
try {
|
||||
await apiPauseSchedule<true>({
|
||||
path: { id: scheduleId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error pausing schedule ${scheduleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function unpauseSchedule(scheduleId: string): Promise<void> {
|
||||
try {
|
||||
await apiUnpauseSchedule<true>({
|
||||
path: { id: scheduleId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error unpausing schedule ${scheduleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateSchedule(scheduleId: string, cron: string): Promise<ScheduledJob> {
|
||||
try {
|
||||
const response = await apiUpdateSchedule<true>({
|
||||
path: { id: scheduleId },
|
||||
body: { cron },
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
return response.data as ScheduledJob;
|
||||
}
|
||||
console.error('Unexpected response format from apiUpdateSchedule', response);
|
||||
throw new Error('Failed to update schedule: Unexpected response format');
|
||||
} catch (error) {
|
||||
console.error(`Error updating schedule ${scheduleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export interface KillJobResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface InspectJobResponse {
|
||||
sessionId?: string | null;
|
||||
processStartTime?: string | null;
|
||||
runningDurationSeconds?: number | null;
|
||||
}
|
||||
|
||||
export async function killRunningJob(scheduleId: string): Promise<KillJobResponse> {
|
||||
try {
|
||||
const response = await apiKillRunningJob<true>({
|
||||
path: { id: scheduleId },
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
return response.data as KillJobResponse;
|
||||
}
|
||||
console.error('Unexpected response format from apiKillRunningJob', response);
|
||||
throw new Error('Failed to kill running job: Unexpected response format');
|
||||
} catch (error) {
|
||||
console.error(`Error killing running job ${scheduleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function inspectRunningJob(scheduleId: string): Promise<InspectJobResponse> {
|
||||
try {
|
||||
const response = await apiInspectRunningJob<true>({
|
||||
path: { id: scheduleId },
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
return response.data as InspectJobResponse;
|
||||
}
|
||||
console.error('Unexpected response format from apiInspectRunningJob', response);
|
||||
throw new Error('Failed to inspect running job: Unexpected response format');
|
||||
} catch (error) {
|
||||
console.error(`Error inspecting running job ${scheduleId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ import type {
|
|||
AddConfigExtensionRequest_unstable,
|
||||
AddSessionExtensionRequest_unstable,
|
||||
ArchiveSessionRequest_unstable,
|
||||
CreateScheduleRequest_unstable,
|
||||
CreateScheduleResponse_unstable,
|
||||
CreateSourceRequest_unstable,
|
||||
CreateSourceResponse_unstable,
|
||||
CustomProviderCreateRequest_unstable,
|
||||
|
|
@ -28,6 +30,7 @@ import type {
|
|||
DefaultsReadResponse_unstable,
|
||||
DefaultsSaveRequest_unstable,
|
||||
DeleteRecipeRequest_unstable,
|
||||
DeleteScheduleRequest_unstable,
|
||||
DeleteSessionRequest,
|
||||
DeleteSourceRequest_unstable,
|
||||
DiagnosticsGetRequest_unstable,
|
||||
|
|
@ -69,12 +72,20 @@ import type {
|
|||
ImportSessionResponse_unstable,
|
||||
ImportSourcesRequest_unstable,
|
||||
ImportSourcesResponse_unstable,
|
||||
InspectRunningJobRequest_unstable,
|
||||
InspectRunningJobResponse_unstable,
|
||||
KillRunningJobRequest_unstable,
|
||||
KillRunningJobResponse_unstable,
|
||||
ListAgentMentionsRequest_unstable,
|
||||
ListAgentMentionsResponse_unstable,
|
||||
ListProvidersRequest_unstable,
|
||||
ListProvidersResponse_unstable,
|
||||
ListRecipesRequest_unstable,
|
||||
ListRecipesResponse_unstable,
|
||||
ListScheduleSessionsRequest_unstable,
|
||||
ListScheduleSessionsResponse_unstable,
|
||||
ListSchedulesRequest_unstable,
|
||||
ListSchedulesResponse_unstable,
|
||||
ListSlashCommandsRequest_unstable,
|
||||
ListSlashCommandsResponse_unstable,
|
||||
ListSourcesRequest_unstable,
|
||||
|
|
@ -85,6 +96,7 @@ import type {
|
|||
OnboardingImportScanResponse_unstable,
|
||||
ParseRecipeRequest_unstable,
|
||||
ParseRecipeResponse_unstable,
|
||||
PauseScheduleRequest_unstable,
|
||||
PreferencesReadRequest_unstable,
|
||||
PreferencesReadResponse_unstable,
|
||||
PreferencesRemoveRequest_unstable,
|
||||
|
|
@ -116,6 +128,8 @@ import type {
|
|||
RemoveSessionExtensionRequest_unstable,
|
||||
RenameSessionRequest_unstable,
|
||||
RequestRecipeParams_unstable,
|
||||
RunScheduleNowRequest_unstable,
|
||||
RunScheduleNowResponse_unstable,
|
||||
SaveRecipeRequest_unstable,
|
||||
SaveRecipeResponse_unstable,
|
||||
ScanRecipeRequest_unstable,
|
||||
|
|
@ -128,12 +142,16 @@ import type {
|
|||
SteerSessionResponse_unstable,
|
||||
TruncateSessionConversationRequest_unstable,
|
||||
UnarchiveSessionRequest_unstable,
|
||||
UnpauseScheduleRequest_unstable,
|
||||
UpdateScheduleRequest_unstable,
|
||||
UpdateScheduleResponse_unstable,
|
||||
UpdateSessionProjectRequest_unstable,
|
||||
UpdateSourceRequest_unstable,
|
||||
UpdateSourceResponse_unstable,
|
||||
UpdateWorkingDirRequest_unstable,
|
||||
} from './types.gen.js';
|
||||
import {
|
||||
zCreateScheduleResponse_unstable,
|
||||
zCreateSourceResponse_unstable,
|
||||
zCustomProviderCreateResponse_unstable,
|
||||
zCustomProviderDeleteResponse_unstable,
|
||||
|
|
@ -158,9 +176,13 @@ import {
|
|||
zGooseToolCallResponse_unstable,
|
||||
zImportSessionResponse_unstable,
|
||||
zImportSourcesResponse_unstable,
|
||||
zInspectRunningJobResponse_unstable,
|
||||
zKillRunningJobResponse_unstable,
|
||||
zListAgentMentionsResponse_unstable,
|
||||
zListProvidersResponse_unstable,
|
||||
zListRecipesResponse_unstable,
|
||||
zListScheduleSessionsResponse_unstable,
|
||||
zListSchedulesResponse_unstable,
|
||||
zListSlashCommandsResponse_unstable,
|
||||
zListSourcesResponse_unstable,
|
||||
zOnboardingImportApplyResponse_unstable,
|
||||
|
|
@ -178,9 +200,11 @@ import {
|
|||
zRecipeToYamlResponse_unstable,
|
||||
zRefreshProviderInventoryResponse_unstable,
|
||||
zRequestRecipeParams_unstable,
|
||||
zRunScheduleNowResponse_unstable,
|
||||
zSaveRecipeResponse_unstable,
|
||||
zScanRecipeResponse_unstable,
|
||||
zSteerSessionResponse_unstable,
|
||||
zUpdateScheduleResponse_unstable,
|
||||
zUpdateSourceResponse_unstable,
|
||||
} from './zod.gen.js';
|
||||
|
||||
|
|
@ -714,6 +738,108 @@ export class GooseExtClient {
|
|||
) as RecipeToYamlResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesList_unstable(
|
||||
params: ListSchedulesRequest_unstable,
|
||||
): Promise<ListSchedulesResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/list",
|
||||
params,
|
||||
);
|
||||
return zListSchedulesResponse_unstable.parse(
|
||||
raw,
|
||||
) as ListSchedulesResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesSessionsList_unstable(
|
||||
params: ListScheduleSessionsRequest_unstable,
|
||||
): Promise<ListScheduleSessionsResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/sessions/list",
|
||||
params,
|
||||
);
|
||||
return zListScheduleSessionsResponse_unstable.parse(
|
||||
raw,
|
||||
) as ListScheduleSessionsResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesCreate_unstable(
|
||||
params: CreateScheduleRequest_unstable,
|
||||
): Promise<CreateScheduleResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/create",
|
||||
params,
|
||||
);
|
||||
return zCreateScheduleResponse_unstable.parse(
|
||||
raw,
|
||||
) as CreateScheduleResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesDelete_unstable(
|
||||
params: DeleteScheduleRequest_unstable,
|
||||
): Promise<void> {
|
||||
await this.conn.extMethod("_goose/unstable/schedules/delete", params);
|
||||
}
|
||||
|
||||
async schedulesPause_unstable(
|
||||
params: PauseScheduleRequest_unstable,
|
||||
): Promise<void> {
|
||||
await this.conn.extMethod("_goose/unstable/schedules/pause", params);
|
||||
}
|
||||
|
||||
async schedulesUnpause_unstable(
|
||||
params: UnpauseScheduleRequest_unstable,
|
||||
): Promise<void> {
|
||||
await this.conn.extMethod("_goose/unstable/schedules/unpause", params);
|
||||
}
|
||||
|
||||
async schedulesUpdate_unstable(
|
||||
params: UpdateScheduleRequest_unstable,
|
||||
): Promise<UpdateScheduleResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/update",
|
||||
params,
|
||||
);
|
||||
return zUpdateScheduleResponse_unstable.parse(
|
||||
raw,
|
||||
) as UpdateScheduleResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesRunNow_unstable(
|
||||
params: RunScheduleNowRequest_unstable,
|
||||
): Promise<RunScheduleNowResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/run-now",
|
||||
params,
|
||||
);
|
||||
return zRunScheduleNowResponse_unstable.parse(
|
||||
raw,
|
||||
) as RunScheduleNowResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesRunningJobKill_unstable(
|
||||
params: KillRunningJobRequest_unstable,
|
||||
): Promise<KillRunningJobResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/running-job/kill",
|
||||
params,
|
||||
);
|
||||
return zKillRunningJobResponse_unstable.parse(
|
||||
raw,
|
||||
) as KillRunningJobResponse_unstable;
|
||||
}
|
||||
|
||||
async schedulesRunningJobInspect_unstable(
|
||||
params: InspectRunningJobRequest_unstable,
|
||||
): Promise<InspectRunningJobResponse_unstable> {
|
||||
const raw = await this.conn.extMethod(
|
||||
"_goose/unstable/schedules/running-job/inspect",
|
||||
params,
|
||||
);
|
||||
return zInspectRunningJobResponse_unstable.parse(
|
||||
raw,
|
||||
) as InspectRunningJobResponse_unstable;
|
||||
}
|
||||
|
||||
async sessionInfo_unstable(
|
||||
params: GetSessionInfoRequest_unstable,
|
||||
): Promise<GetSessionInfoResponse_unstable> {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1335,15 +1335,32 @@ export type RecipeToYamlResponse_unstable = {
|
|||
yaml: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list-style metadata for a single session without loading the conversation.
|
||||
*/
|
||||
export type GetSessionInfoRequest_unstable = {
|
||||
sessionId: string;
|
||||
export type ListSchedulesRequest_unstable = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type GetSessionInfoResponse_unstable = {
|
||||
session: SessionInfo;
|
||||
export type ListSchedulesResponse_unstable = {
|
||||
jobs: Array<ScheduledJobDto>;
|
||||
};
|
||||
|
||||
export type ScheduledJobDto = {
|
||||
id: string;
|
||||
source: string;
|
||||
cron: string;
|
||||
lastRun?: string | null;
|
||||
currentlyRunning: boolean;
|
||||
paused: boolean;
|
||||
currentSessionId?: string | null;
|
||||
jobStartTime?: string | null;
|
||||
};
|
||||
|
||||
export type ListScheduleSessionsRequest_unstable = {
|
||||
scheduleId: string;
|
||||
limit: number;
|
||||
};
|
||||
|
||||
export type ListScheduleSessionsResponse_unstable = {
|
||||
sessions: Array<SessionInfo>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1398,6 +1415,78 @@ export type SessionInfo = {
|
|||
*/
|
||||
export type SessionId = string;
|
||||
|
||||
export type CreateScheduleRequest_unstable = {
|
||||
id: string;
|
||||
recipe: RecipeDto;
|
||||
cron: string;
|
||||
};
|
||||
|
||||
export type CreateScheduleResponse_unstable = {
|
||||
job: ScheduledJobDto;
|
||||
};
|
||||
|
||||
export type DeleteScheduleRequest_unstable = {
|
||||
scheduleId: string;
|
||||
};
|
||||
|
||||
export type PauseScheduleRequest_unstable = {
|
||||
scheduleId: string;
|
||||
};
|
||||
|
||||
export type UnpauseScheduleRequest_unstable = {
|
||||
scheduleId: string;
|
||||
};
|
||||
|
||||
export type UpdateScheduleRequest_unstable = {
|
||||
scheduleId: string;
|
||||
cron: string;
|
||||
};
|
||||
|
||||
export type UpdateScheduleResponse_unstable = {
|
||||
job: ScheduledJobDto;
|
||||
};
|
||||
|
||||
export type RunScheduleNowRequest_unstable = {
|
||||
scheduleId: string;
|
||||
};
|
||||
|
||||
export type RunScheduleNowResponse_unstable = {
|
||||
status: RunScheduleNowStatus;
|
||||
sessionId?: string | null;
|
||||
};
|
||||
|
||||
export type RunScheduleNowStatus = 'completed' | 'cancelled';
|
||||
|
||||
export type KillRunningJobRequest_unstable = {
|
||||
jobId: string;
|
||||
};
|
||||
|
||||
export type KillRunningJobResponse_unstable = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type InspectRunningJobRequest_unstable = {
|
||||
jobId: string;
|
||||
};
|
||||
|
||||
export type InspectRunningJobResponse_unstable = {
|
||||
running: boolean;
|
||||
sessionId?: string | null;
|
||||
jobStartTime?: string | null;
|
||||
runningDurationSeconds?: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list-style metadata for a single session without loading the conversation.
|
||||
*/
|
||||
export type GetSessionInfoRequest_unstable = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
export type GetSessionInfoResponse_unstable = {
|
||||
session: SessionInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* Truncate a session conversation from the given message timestamp onward.
|
||||
*/
|
||||
|
|
@ -1905,14 +1994,14 @@ export type RecipeParamsAction = 'submit' | 'cancel';
|
|||
export type ExtRequest = {
|
||||
id: string;
|
||||
method: string;
|
||||
params?: AddSessionExtensionRequest_unstable | RemoveSessionExtensionRequest_unstable | GetToolsRequest_unstable | GooseToolCallRequest_unstable | ReadResourceRequest_unstable | UpdateWorkingDirRequest_unstable | SetSessionSystemPromptRequest_unstable | SteerSessionRequest_unstable | DiagnosticsGetRequest_unstable | DeleteSessionRequest | GetConfigExtensionsRequest_unstable | GetAvailableExtensionsRequest_unstable | AddConfigExtensionRequest_unstable | RemoveConfigExtensionRequest_unstable | SetConfigExtensionEnabledRequest_unstable | GetSessionExtensionsRequest_unstable | ListProvidersRequest_unstable | ProviderSupportedModelsListRequest_unstable | ProviderCatalogListRequest_unstable | ProviderSetupCatalogListRequest_unstable | ProviderCatalogTemplateRequest_unstable | CustomProviderCreateRequest_unstable | CustomProviderReadRequest_unstable | CustomProviderUpdateRequest_unstable | CustomProviderDeleteRequest_unstable | RefreshProviderInventoryRequest_unstable | ProviderConfigReadRequest_unstable | ProviderConfigStatusRequest_unstable | ProviderConfigSaveRequest_unstable | ProviderConfigDeleteRequest_unstable | ProviderConfigAuthenticateRequest_unstable | PreferencesReadRequest_unstable | PreferencesSaveRequest_unstable | PreferencesRemoveRequest_unstable | DefaultsReadRequest_unstable | DefaultsSaveRequest_unstable | OnboardingImportScanRequest_unstable | OnboardingImportApplyRequest_unstable | ExportSessionRequest_unstable | ImportSessionRequest_unstable | EncodeRecipeRequest_unstable | DecodeRecipeRequest_unstable | ScanRecipeRequest_unstable | ListRecipesRequest_unstable | DeleteRecipeRequest_unstable | ScheduleRecipeRequest_unstable | SetRecipeSlashCommandRequest_unstable | SaveRecipeRequest_unstable | ParseRecipeRequest_unstable | RecipeToYamlRequest_unstable | GetSessionInfoRequest_unstable | TruncateSessionConversationRequest_unstable | UpdateSessionProjectRequest_unstable | RenameSessionRequest_unstable | ArchiveSessionRequest_unstable | UnarchiveSessionRequest_unstable | CreateSourceRequest_unstable | ListSourcesRequest_unstable | ListAgentMentionsRequest_unstable | ListSlashCommandsRequest_unstable | UpdateSourceRequest_unstable | DeleteSourceRequest_unstable | ExportSourceRequest_unstable | ImportSourcesRequest_unstable | DictationTranscribeRequest_unstable | DictationConfigRequest_unstable | DictationSecretSaveRequest_unstable | DictationSecretDeleteRequest_unstable | DictationModelsListRequest_unstable | DictationModelDownloadRequest_unstable | DictationModelDownloadProgressRequest_unstable | DictationModelCancelRequest_unstable | DictationModelDeleteRequest_unstable | DictationModelSelectRequest_unstable | {
|
||||
params?: AddSessionExtensionRequest_unstable | RemoveSessionExtensionRequest_unstable | GetToolsRequest_unstable | GooseToolCallRequest_unstable | ReadResourceRequest_unstable | UpdateWorkingDirRequest_unstable | SetSessionSystemPromptRequest_unstable | SteerSessionRequest_unstable | DiagnosticsGetRequest_unstable | DeleteSessionRequest | GetConfigExtensionsRequest_unstable | GetAvailableExtensionsRequest_unstable | AddConfigExtensionRequest_unstable | RemoveConfigExtensionRequest_unstable | SetConfigExtensionEnabledRequest_unstable | GetSessionExtensionsRequest_unstable | ListProvidersRequest_unstable | ProviderSupportedModelsListRequest_unstable | ProviderCatalogListRequest_unstable | ProviderSetupCatalogListRequest_unstable | ProviderCatalogTemplateRequest_unstable | CustomProviderCreateRequest_unstable | CustomProviderReadRequest_unstable | CustomProviderUpdateRequest_unstable | CustomProviderDeleteRequest_unstable | RefreshProviderInventoryRequest_unstable | ProviderConfigReadRequest_unstable | ProviderConfigStatusRequest_unstable | ProviderConfigSaveRequest_unstable | ProviderConfigDeleteRequest_unstable | ProviderConfigAuthenticateRequest_unstable | PreferencesReadRequest_unstable | PreferencesSaveRequest_unstable | PreferencesRemoveRequest_unstable | DefaultsReadRequest_unstable | DefaultsSaveRequest_unstable | OnboardingImportScanRequest_unstable | OnboardingImportApplyRequest_unstable | ExportSessionRequest_unstable | ImportSessionRequest_unstable | EncodeRecipeRequest_unstable | DecodeRecipeRequest_unstable | ScanRecipeRequest_unstable | ListRecipesRequest_unstable | DeleteRecipeRequest_unstable | ScheduleRecipeRequest_unstable | SetRecipeSlashCommandRequest_unstable | SaveRecipeRequest_unstable | ParseRecipeRequest_unstable | RecipeToYamlRequest_unstable | ListSchedulesRequest_unstable | ListScheduleSessionsRequest_unstable | CreateScheduleRequest_unstable | DeleteScheduleRequest_unstable | PauseScheduleRequest_unstable | UnpauseScheduleRequest_unstable | UpdateScheduleRequest_unstable | RunScheduleNowRequest_unstable | KillRunningJobRequest_unstable | InspectRunningJobRequest_unstable | GetSessionInfoRequest_unstable | TruncateSessionConversationRequest_unstable | UpdateSessionProjectRequest_unstable | RenameSessionRequest_unstable | ArchiveSessionRequest_unstable | UnarchiveSessionRequest_unstable | CreateSourceRequest_unstable | ListSourcesRequest_unstable | ListAgentMentionsRequest_unstable | ListSlashCommandsRequest_unstable | UpdateSourceRequest_unstable | DeleteSourceRequest_unstable | ExportSourceRequest_unstable | ImportSourcesRequest_unstable | DictationTranscribeRequest_unstable | DictationConfigRequest_unstable | DictationSecretSaveRequest_unstable | DictationSecretDeleteRequest_unstable | DictationModelsListRequest_unstable | DictationModelDownloadRequest_unstable | DictationModelDownloadProgressRequest_unstable | DictationModelCancelRequest_unstable | DictationModelDeleteRequest_unstable | DictationModelSelectRequest_unstable | {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type ExtResponse = {
|
||||
id: string;
|
||||
result?: EmptyResponse | GetToolsResponse_unstable | GooseToolCallResponse_unstable | ReadResourceResponse_unstable | SteerSessionResponse_unstable | DiagnosticsGetResponse_unstable | GetConfigExtensionsResponse_unstable | GetAvailableExtensionsResponse_unstable | GetSessionExtensionsResponse_unstable | ListProvidersResponse_unstable | ProviderSupportedModelsListResponse_unstable | ProviderCatalogListResponse_unstable | ProviderSetupCatalogListResponse_unstable | ProviderCatalogTemplateResponse_unstable | CustomProviderCreateResponse_unstable | CustomProviderReadResponse_unstable | CustomProviderUpdateResponse_unstable | CustomProviderDeleteResponse_unstable | RefreshProviderInventoryResponse_unstable | ProviderConfigReadResponse_unstable | ProviderConfigStatusResponse_unstable | ProviderConfigChangeResponse_unstable | PreferencesReadResponse_unstable | DefaultsReadResponse_unstable | OnboardingImportScanResponse_unstable | OnboardingImportApplyResponse_unstable | ExportSessionResponse_unstable | ImportSessionResponse_unstable | EncodeRecipeResponse_unstable | DecodeRecipeResponse_unstable | ScanRecipeResponse_unstable | ListRecipesResponse_unstable | SaveRecipeResponse_unstable | ParseRecipeResponse_unstable | RecipeToYamlResponse_unstable | GetSessionInfoResponse_unstable | CreateSourceResponse_unstable | ListSourcesResponse_unstable | ListAgentMentionsResponse_unstable | ListSlashCommandsResponse_unstable | UpdateSourceResponse_unstable | ExportSourceResponse_unstable | ImportSourcesResponse_unstable | DictationTranscribeResponse_unstable | DictationConfigResponse_unstable | DictationModelsListResponse_unstable | DictationModelDownloadProgressResponse_unstable | unknown;
|
||||
result?: EmptyResponse | GetToolsResponse_unstable | GooseToolCallResponse_unstable | ReadResourceResponse_unstable | SteerSessionResponse_unstable | DiagnosticsGetResponse_unstable | GetConfigExtensionsResponse_unstable | GetAvailableExtensionsResponse_unstable | GetSessionExtensionsResponse_unstable | ListProvidersResponse_unstable | ProviderSupportedModelsListResponse_unstable | ProviderCatalogListResponse_unstable | ProviderSetupCatalogListResponse_unstable | ProviderCatalogTemplateResponse_unstable | CustomProviderCreateResponse_unstable | CustomProviderReadResponse_unstable | CustomProviderUpdateResponse_unstable | CustomProviderDeleteResponse_unstable | RefreshProviderInventoryResponse_unstable | ProviderConfigReadResponse_unstable | ProviderConfigStatusResponse_unstable | ProviderConfigChangeResponse_unstable | PreferencesReadResponse_unstable | DefaultsReadResponse_unstable | OnboardingImportScanResponse_unstable | OnboardingImportApplyResponse_unstable | ExportSessionResponse_unstable | ImportSessionResponse_unstable | EncodeRecipeResponse_unstable | DecodeRecipeResponse_unstable | ScanRecipeResponse_unstable | ListRecipesResponse_unstable | SaveRecipeResponse_unstable | ParseRecipeResponse_unstable | RecipeToYamlResponse_unstable | ListSchedulesResponse_unstable | ListScheduleSessionsResponse_unstable | CreateScheduleResponse_unstable | UpdateScheduleResponse_unstable | RunScheduleNowResponse_unstable | KillRunningJobResponse_unstable | InspectRunningJobResponse_unstable | GetSessionInfoResponse_unstable | CreateSourceResponse_unstable | ListSourcesResponse_unstable | ListAgentMentionsResponse_unstable | ListSlashCommandsResponse_unstable | UpdateSourceResponse_unstable | ExportSourceResponse_unstable | ImportSourcesResponse_unstable | DictationTranscribeResponse_unstable | DictationConfigResponse_unstable | DictationModelsListResponse_unstable | DictationModelDownloadProgressResponse_unstable | unknown;
|
||||
} | {
|
||||
error: {
|
||||
code: number;
|
||||
|
|
|
|||
|
|
@ -1470,11 +1470,35 @@ export const zRecipeToYamlResponse_unstable = z.object({
|
|||
yaml: z.string()
|
||||
});
|
||||
|
||||
/**
|
||||
* Return list-style metadata for a single session without loading the conversation.
|
||||
*/
|
||||
export const zGetSessionInfoRequest_unstable = z.object({
|
||||
sessionId: z.string()
|
||||
export const zListSchedulesRequest_unstable = z.record(z.unknown());
|
||||
|
||||
export const zScheduledJobDto = z.object({
|
||||
id: z.string(),
|
||||
source: z.string(),
|
||||
cron: z.string(),
|
||||
lastRun: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
currentlyRunning: z.boolean(),
|
||||
paused: z.boolean(),
|
||||
currentSessionId: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
jobStartTime: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional()
|
||||
});
|
||||
|
||||
export const zListSchedulesResponse_unstable = z.object({
|
||||
jobs: z.array(zScheduledJobDto)
|
||||
});
|
||||
|
||||
export const zListScheduleSessionsRequest_unstable = z.object({
|
||||
scheduleId: z.string(),
|
||||
limit: z.number().int().gte(0)
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -1508,6 +1532,90 @@ export const zSessionInfo = z.object({
|
|||
]).optional()
|
||||
});
|
||||
|
||||
export const zListScheduleSessionsResponse_unstable = z.object({
|
||||
sessions: z.array(zSessionInfo)
|
||||
});
|
||||
|
||||
export const zCreateScheduleRequest_unstable = z.object({
|
||||
id: z.string(),
|
||||
recipe: zRecipeDto,
|
||||
cron: z.string()
|
||||
});
|
||||
|
||||
export const zCreateScheduleResponse_unstable = z.object({
|
||||
job: zScheduledJobDto
|
||||
});
|
||||
|
||||
export const zDeleteScheduleRequest_unstable = z.object({
|
||||
scheduleId: z.string()
|
||||
});
|
||||
|
||||
export const zPauseScheduleRequest_unstable = z.object({
|
||||
scheduleId: z.string()
|
||||
});
|
||||
|
||||
export const zUnpauseScheduleRequest_unstable = z.object({
|
||||
scheduleId: z.string()
|
||||
});
|
||||
|
||||
export const zUpdateScheduleRequest_unstable = z.object({
|
||||
scheduleId: z.string(),
|
||||
cron: z.string()
|
||||
});
|
||||
|
||||
export const zUpdateScheduleResponse_unstable = z.object({
|
||||
job: zScheduledJobDto
|
||||
});
|
||||
|
||||
export const zRunScheduleNowRequest_unstable = z.object({
|
||||
scheduleId: z.string()
|
||||
});
|
||||
|
||||
export const zRunScheduleNowStatus = z.enum(['completed', 'cancelled']);
|
||||
|
||||
export const zRunScheduleNowResponse_unstable = z.object({
|
||||
status: zRunScheduleNowStatus,
|
||||
sessionId: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional()
|
||||
});
|
||||
|
||||
export const zKillRunningJobRequest_unstable = z.object({
|
||||
jobId: z.string()
|
||||
});
|
||||
|
||||
export const zKillRunningJobResponse_unstable = z.object({
|
||||
message: z.string()
|
||||
});
|
||||
|
||||
export const zInspectRunningJobRequest_unstable = z.object({
|
||||
jobId: z.string()
|
||||
});
|
||||
|
||||
export const zInspectRunningJobResponse_unstable = z.object({
|
||||
running: z.boolean(),
|
||||
sessionId: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
jobStartTime: z.union([
|
||||
z.string(),
|
||||
z.null()
|
||||
]).optional(),
|
||||
runningDurationSeconds: z.union([
|
||||
z.number().int(),
|
||||
z.null()
|
||||
]).optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Return list-style metadata for a single session without loading the conversation.
|
||||
*/
|
||||
export const zGetSessionInfoRequest_unstable = z.object({
|
||||
sessionId: z.string()
|
||||
});
|
||||
|
||||
export const zGetSessionInfoResponse_unstable = z.object({
|
||||
session: zSessionInfo
|
||||
});
|
||||
|
|
@ -2056,6 +2164,16 @@ export const zExtRequest = z.object({
|
|||
zSaveRecipeRequest_unstable,
|
||||
zParseRecipeRequest_unstable,
|
||||
zRecipeToYamlRequest_unstable,
|
||||
zListSchedulesRequest_unstable,
|
||||
zListScheduleSessionsRequest_unstable,
|
||||
zCreateScheduleRequest_unstable,
|
||||
zDeleteScheduleRequest_unstable,
|
||||
zPauseScheduleRequest_unstable,
|
||||
zUnpauseScheduleRequest_unstable,
|
||||
zUpdateScheduleRequest_unstable,
|
||||
zRunScheduleNowRequest_unstable,
|
||||
zKillRunningJobRequest_unstable,
|
||||
zInspectRunningJobRequest_unstable,
|
||||
zGetSessionInfoRequest_unstable,
|
||||
zTruncateSessionConversationRequest_unstable,
|
||||
zUpdateSessionProjectRequest_unstable,
|
||||
|
|
@ -2128,6 +2246,13 @@ export const zExtResponse = z.union([
|
|||
zSaveRecipeResponse_unstable,
|
||||
zParseRecipeResponse_unstable,
|
||||
zRecipeToYamlResponse_unstable,
|
||||
zListSchedulesResponse_unstable,
|
||||
zListScheduleSessionsResponse_unstable,
|
||||
zCreateScheduleResponse_unstable,
|
||||
zUpdateScheduleResponse_unstable,
|
||||
zRunScheduleNowResponse_unstable,
|
||||
zKillRunningJobResponse_unstable,
|
||||
zInspectRunningJobResponse_unstable,
|
||||
zGetSessionInfoResponse_unstable,
|
||||
zCreateSourceResponse_unstable,
|
||||
zListSourcesResponse_unstable,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue