1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#![allow(
clippy::wildcard_imports,
clippy::too_many_arguments,
clippy::needless_pass_by_value,
dead_code,
reason = "split-out module preserves existing code while keeping files under the linecheck limit"
)]
use super::*;
/// Routine operations, each mapping to a `/api/v1/routines` REST route.
#[derive(Subcommand)]
pub(crate) enum RoutineCmd {
/// Create a new routine.
Create {
/// Cron expression (host local timezone, not UTC). Repeat to set multiple schedules.
#[arg(long, required = true)]
schedule: Vec<String>,
/// Human-readable title.
#[arg(long)]
title: String,
/// Agent registry key to launch.
#[arg(long)]
agent: String,
/// Model ID to run the agent with (e.g. `claude-sonnet-4-6`); omit to use the agent's own
/// default.
#[arg(long)]
model: Option<String>,
/// Task prompt.
#[arg(long)]
prompt: String,
/// Short (≤5 line) statement of the routine's goal — the "why" behind the prompt.
#[arg(long)]
goal: Option<String>,
/// Repositories as a JSON array (e.g. `[{"repository":"url","branch":"main"}]`).
#[arg(long)]
repositories: Option<String>,
/// Machines to run this routine on, as a JSON array (e.g. `["work","server"]`). Empty/omitted
/// means the routine runs on no machine until assigned.
#[arg(long)]
machines: Option<String>,
/// Workbench TTL in seconds for finished runs.
#[arg(long)]
ttl_secs: Option<u64>,
/// Max runtime in seconds before the watchdog kills a run.
#[arg(long)]
max_runtime_secs: Option<u64>,
/// Tag for the routine; repeat the flag to add several.
#[arg(long = "tag")]
tags: Vec<String>,
/// Create the routine disabled instead of enabled (the default).
#[arg(long)]
disabled: bool,
},
/// List all routines.
List,
/// Get a single routine by ID.
Get {
/// UUID of the routine.
id: String,
},
/// Update fields of an existing routine (only the flags you pass change).
Update {
/// UUID of the routine to update.
id: String,
/// New cron expression (host local timezone, not UTC). Repeat to replace all schedules.
#[arg(long)]
schedule: Vec<String>,
/// New title.
#[arg(long)]
title: Option<String>,
/// New agent registry key.
#[arg(long)]
agent: Option<String>,
/// New model ID, or an empty string to clear the override back to the agent's own default.
#[arg(long)]
model: Option<String>,
/// New prompt.
#[arg(long)]
prompt: Option<String>,
/// New goal (≤5 lines), or an empty string to clear it. Omit to keep the existing value.
#[arg(long)]
goal: Option<String>,
/// New repositories as a JSON array.
#[arg(long)]
repositories: Option<String>,
/// New machines targeting list as a JSON array (e.g. `["work","server"]`).
#[arg(long)]
machines: Option<String>,
/// New enabled state (`true`/`false`).
#[arg(long)]
enabled: Option<bool>,
/// New workbench TTL in seconds.
#[arg(long)]
ttl_secs: Option<u64>,
/// New max runtime in seconds.
#[arg(long)]
max_runtime_secs: Option<u64>,
/// Replacement tag; repeat the flag to set several. Passing any `--tag` replaces the whole
/// tag list; omit it to keep the existing tags.
#[arg(long = "tag")]
tags: Vec<String>,
},
/// Replace a routine wholesale (all fields, like create but for an existing ID).
Replace {
/// UUID of the routine to replace.
id: String,
/// Cron expression (host local timezone, not UTC). Repeat to set multiple schedules.
#[arg(long, required = true)]
schedule: Vec<String>,
/// Human-readable title.
#[arg(long)]
title: String,
/// Agent registry key to launch.
#[arg(long)]
agent: String,
/// Model ID to run the agent with (e.g. `claude-sonnet-4-6`); omit to use the agent's own
/// default.
#[arg(long)]
model: Option<String>,
/// Task prompt.
#[arg(long)]
prompt: String,
/// Short (≤5 line) statement of the routine's goal — the "why" behind the prompt.
#[arg(long)]
goal: Option<String>,
/// Repositories as a JSON array.
#[arg(long)]
repositories: Option<String>,
/// Machines to run this routine on, as a JSON array (e.g. `["work","server"]`).
#[arg(long)]
machines: Option<String>,
/// Workbench TTL in seconds for finished runs.
#[arg(long)]
ttl_secs: Option<u64>,
/// Max runtime in seconds before the watchdog kills a run.
#[arg(long)]
max_runtime_secs: Option<u64>,
/// Tag for the routine; repeat the flag to add several.
#[arg(long = "tag")]
tags: Vec<String>,
/// Replace into a disabled state instead of enabled (the default).
#[arg(long)]
disabled: bool,
},
/// Delete a routine by ID.
Delete {
/// UUID of the routine to delete.
id: String,
},
/// Move a routine to another filesystem folder and/or slug.
Move {
/// Routine id, slug, or relative path to move.
id: String,
/// New parent folder relative to `routines/`; omit or pass blank for the root.
#[arg(long)]
folder: Option<String>,
/// New routine directory name inside the folder. Omit to preserve the current slug.
#[arg(long)]
slug: Option<String>,
},
/// Manually trigger a routine outside its schedule.
Trigger {
/// UUID of the routine to trigger.
id: String,
},
/// Print a routine's newest run log.
Logs {
/// UUID of the routine whose logs to print.
id: String,
},
/// Print the iCalendar feed of upcoming routine fire times.
Ical,
}
include!("run.rs");