@orchagent/cli 0.3.52 → 0.3.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/index.js +2 -0
- package/dist/commands/schedule.js +297 -0
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -28,6 +28,7 @@ const test_1 = require("./test");
|
|
|
28
28
|
const security_1 = require("./security");
|
|
29
29
|
const billing_1 = require("./billing");
|
|
30
30
|
const agent_keys_1 = require("./agent-keys");
|
|
31
|
+
const schedule_1 = require("./schedule");
|
|
31
32
|
function registerCommands(program) {
|
|
32
33
|
(0, login_1.registerLoginCommand)(program);
|
|
33
34
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -56,4 +57,5 @@ function registerCommands(program) {
|
|
|
56
57
|
(0, security_1.registerSecurityCommand)(program);
|
|
57
58
|
(0, billing_1.registerBillingCommand)(program);
|
|
58
59
|
(0, agent_keys_1.registerAgentKeysCommand)(program);
|
|
60
|
+
(0, schedule_1.registerScheduleCommand)(program);
|
|
59
61
|
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerScheduleCommand = registerScheduleCommand;
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const config_1 = require("../lib/config");
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const errors_1 = require("../lib/errors");
|
|
12
|
+
const output_1 = require("../lib/output");
|
|
13
|
+
const agent_ref_1 = require("../lib/agent-ref");
|
|
14
|
+
const api_2 = require("../lib/api");
|
|
15
|
+
// ============================================
|
|
16
|
+
// HELPERS
|
|
17
|
+
// ============================================
|
|
18
|
+
async function resolveWorkspaceId(config, slug) {
|
|
19
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
20
|
+
const targetSlug = slug ?? configFile.workspace;
|
|
21
|
+
if (!targetSlug) {
|
|
22
|
+
throw new errors_1.CliError('No workspace specified. Use --workspace <slug> or run `orch workspace use <slug>` first.');
|
|
23
|
+
}
|
|
24
|
+
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
25
|
+
const workspace = response.workspaces.find((w) => w.slug === targetSlug);
|
|
26
|
+
if (!workspace) {
|
|
27
|
+
throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
|
|
28
|
+
}
|
|
29
|
+
return workspace.id;
|
|
30
|
+
}
|
|
31
|
+
function formatDate(iso) {
|
|
32
|
+
if (!iso)
|
|
33
|
+
return '-';
|
|
34
|
+
return new Date(iso).toLocaleString();
|
|
35
|
+
}
|
|
36
|
+
function statusColor(status) {
|
|
37
|
+
if (!status)
|
|
38
|
+
return '-';
|
|
39
|
+
switch (status) {
|
|
40
|
+
case 'completed': return chalk_1.default.green(status);
|
|
41
|
+
case 'failed': return chalk_1.default.red(status);
|
|
42
|
+
case 'running': return chalk_1.default.yellow(status);
|
|
43
|
+
default: return status;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ============================================
|
|
47
|
+
// COMMAND REGISTRATION
|
|
48
|
+
// ============================================
|
|
49
|
+
function registerScheduleCommand(program) {
|
|
50
|
+
const schedule = program
|
|
51
|
+
.command('schedule')
|
|
52
|
+
.description('Manage scheduled agent runs (cron and webhooks)');
|
|
53
|
+
// orch schedule list
|
|
54
|
+
schedule
|
|
55
|
+
.command('list')
|
|
56
|
+
.description('List schedules in your workspace')
|
|
57
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
58
|
+
.option('--agent <name>', 'Filter by agent name')
|
|
59
|
+
.option('--type <type>', 'Filter by type (cron or webhook)')
|
|
60
|
+
.option('--json', 'Output as JSON')
|
|
61
|
+
.action(async (options) => {
|
|
62
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
63
|
+
if (!config.apiKey) {
|
|
64
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
65
|
+
}
|
|
66
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
if (options.agent)
|
|
69
|
+
params.set('agent_id', options.agent);
|
|
70
|
+
if (options.type)
|
|
71
|
+
params.set('schedule_type', options.type);
|
|
72
|
+
params.set('limit', '100');
|
|
73
|
+
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
74
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules${qs}`);
|
|
75
|
+
if (options.json) {
|
|
76
|
+
(0, output_1.printJson)(result);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (result.schedules.length === 0) {
|
|
80
|
+
process.stdout.write('No schedules found.\n');
|
|
81
|
+
process.stdout.write(chalk_1.default.gray('\nCreate one with: orch schedule create <org/agent> --cron "0 9 * * *"\n'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const table = new cli_table3_1.default({
|
|
85
|
+
head: [
|
|
86
|
+
chalk_1.default.bold('ID'),
|
|
87
|
+
chalk_1.default.bold('Agent'),
|
|
88
|
+
chalk_1.default.bold('Type'),
|
|
89
|
+
chalk_1.default.bold('Schedule'),
|
|
90
|
+
chalk_1.default.bold('Enabled'),
|
|
91
|
+
chalk_1.default.bold('Last Run'),
|
|
92
|
+
chalk_1.default.bold('Status'),
|
|
93
|
+
chalk_1.default.bold('Runs'),
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
result.schedules.forEach((s) => {
|
|
97
|
+
table.push([
|
|
98
|
+
s.id.slice(0, 8),
|
|
99
|
+
`${s.agent_name}@${s.agent_version}`,
|
|
100
|
+
s.schedule_type,
|
|
101
|
+
s.schedule_type === 'cron' ? (s.cron_expression ?? '-') : 'webhook',
|
|
102
|
+
s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no'),
|
|
103
|
+
formatDate(s.last_run_at),
|
|
104
|
+
statusColor(s.last_run_status),
|
|
105
|
+
s.run_count.toString(),
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
process.stdout.write(`\n${table.toString()}\n`);
|
|
109
|
+
process.stdout.write(chalk_1.default.gray(`\n${result.total} schedule(s) total\n`));
|
|
110
|
+
});
|
|
111
|
+
// orch schedule create <agent>
|
|
112
|
+
schedule
|
|
113
|
+
.command('create <agent>')
|
|
114
|
+
.description('Create a cron or webhook schedule (org/agent[@version])')
|
|
115
|
+
.option('--cron <expression>', 'Cron expression (e.g., "0 9 * * 1" for every Monday 9am)')
|
|
116
|
+
.option('--webhook', 'Create a webhook-triggered schedule instead of cron')
|
|
117
|
+
.option('--timezone <tz>', 'Timezone for cron schedule (default: UTC)', 'UTC')
|
|
118
|
+
.option('--input <json>', 'Input data as JSON string')
|
|
119
|
+
.option('--provider <provider>', 'LLM provider (anthropic, openai, gemini)')
|
|
120
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
121
|
+
.action(async (agentArg, options) => {
|
|
122
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
123
|
+
if (!config.apiKey) {
|
|
124
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
125
|
+
}
|
|
126
|
+
if (!options.cron && !options.webhook) {
|
|
127
|
+
throw new errors_1.CliError('Specify --cron <expression> or --webhook');
|
|
128
|
+
}
|
|
129
|
+
if (options.cron && options.webhook) {
|
|
130
|
+
throw new errors_1.CliError('Cannot use both --cron and --webhook. Choose one.');
|
|
131
|
+
}
|
|
132
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
133
|
+
const ref = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
134
|
+
// Resolve agent to get the ID
|
|
135
|
+
const agent = await (0, api_2.getAgentWithFallback)(config, ref.org, ref.agent, ref.version);
|
|
136
|
+
// Parse input data
|
|
137
|
+
let inputData;
|
|
138
|
+
if (options.input) {
|
|
139
|
+
try {
|
|
140
|
+
inputData = JSON.parse(options.input);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
throw new errors_1.CliError('Invalid JSON in --input. Use single quotes: --input \'{"key": "value"}\'');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const scheduleType = options.webhook ? 'webhook' : 'cron';
|
|
147
|
+
const body = {
|
|
148
|
+
agent_id: agent.id,
|
|
149
|
+
agent_name: ref.agent,
|
|
150
|
+
agent_version: ref.version === 'latest' ? (agent.version ?? ref.version) : ref.version,
|
|
151
|
+
schedule_type: scheduleType,
|
|
152
|
+
timezone: options.timezone ?? 'UTC',
|
|
153
|
+
};
|
|
154
|
+
if (options.cron)
|
|
155
|
+
body.cron_expression = options.cron;
|
|
156
|
+
if (inputData)
|
|
157
|
+
body.input_data = inputData;
|
|
158
|
+
if (options.provider)
|
|
159
|
+
body.llm_provider = options.provider;
|
|
160
|
+
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules`, {
|
|
161
|
+
body: JSON.stringify(body),
|
|
162
|
+
headers: { 'Content-Type': 'application/json' },
|
|
163
|
+
});
|
|
164
|
+
const s = result.schedule;
|
|
165
|
+
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule created\n\n`);
|
|
166
|
+
process.stdout.write(` ID: ${s.id}\n`);
|
|
167
|
+
process.stdout.write(` Agent: ${s.agent_name}@${s.agent_version}\n`);
|
|
168
|
+
process.stdout.write(` Type: ${s.schedule_type}\n`);
|
|
169
|
+
if (s.schedule_type === 'cron') {
|
|
170
|
+
process.stdout.write(` Cron: ${s.cron_expression}\n`);
|
|
171
|
+
process.stdout.write(` TZ: ${s.timezone}\n`);
|
|
172
|
+
if (s.next_run_at) {
|
|
173
|
+
process.stdout.write(` Next: ${formatDate(s.next_run_at)}\n`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
if (s.webhook_url) {
|
|
178
|
+
process.stdout.write(`\n ${chalk_1.default.bold('Webhook URL')} (save this — secret shown once):\n`);
|
|
179
|
+
process.stdout.write(` ${s.webhook_url}\n`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
process.stdout.write('\n');
|
|
183
|
+
});
|
|
184
|
+
// orch schedule update <schedule-id>
|
|
185
|
+
schedule
|
|
186
|
+
.command('update <schedule-id>')
|
|
187
|
+
.description('Update a schedule')
|
|
188
|
+
.option('--cron <expression>', 'New cron expression')
|
|
189
|
+
.option('--timezone <tz>', 'New timezone')
|
|
190
|
+
.option('--input <json>', 'New input data as JSON')
|
|
191
|
+
.option('--provider <provider>', 'New LLM provider')
|
|
192
|
+
.option('--enable', 'Enable the schedule')
|
|
193
|
+
.option('--disable', 'Disable the schedule')
|
|
194
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
195
|
+
.action(async (scheduleId, options) => {
|
|
196
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
197
|
+
if (!config.apiKey) {
|
|
198
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
199
|
+
}
|
|
200
|
+
if (options.enable && options.disable) {
|
|
201
|
+
throw new errors_1.CliError('Cannot use both --enable and --disable');
|
|
202
|
+
}
|
|
203
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
204
|
+
const updates = {};
|
|
205
|
+
if (options.cron)
|
|
206
|
+
updates.cron_expression = options.cron;
|
|
207
|
+
if (options.timezone)
|
|
208
|
+
updates.timezone = options.timezone;
|
|
209
|
+
if (options.provider)
|
|
210
|
+
updates.llm_provider = options.provider;
|
|
211
|
+
if (options.enable)
|
|
212
|
+
updates.enabled = true;
|
|
213
|
+
if (options.disable)
|
|
214
|
+
updates.enabled = false;
|
|
215
|
+
if (options.input) {
|
|
216
|
+
try {
|
|
217
|
+
updates.input_data = JSON.parse(options.input);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw new errors_1.CliError('Invalid JSON in --input');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (Object.keys(updates).length === 0) {
|
|
224
|
+
throw new errors_1.CliError('Nothing to update. Specify at least one option.');
|
|
225
|
+
}
|
|
226
|
+
const result = await (0, api_1.request)(config, 'PATCH', `/workspaces/${workspaceId}/schedules/${scheduleId}`, {
|
|
227
|
+
body: JSON.stringify(updates),
|
|
228
|
+
headers: { 'Content-Type': 'application/json' },
|
|
229
|
+
});
|
|
230
|
+
const s = result.schedule;
|
|
231
|
+
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule updated\n\n`);
|
|
232
|
+
process.stdout.write(` ID: ${s.id}\n`);
|
|
233
|
+
process.stdout.write(` Enabled: ${s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no')}\n`);
|
|
234
|
+
if (s.cron_expression) {
|
|
235
|
+
process.stdout.write(` Cron: ${s.cron_expression}\n`);
|
|
236
|
+
process.stdout.write(` TZ: ${s.timezone}\n`);
|
|
237
|
+
}
|
|
238
|
+
if (s.next_run_at) {
|
|
239
|
+
process.stdout.write(` Next: ${formatDate(s.next_run_at)}\n`);
|
|
240
|
+
}
|
|
241
|
+
process.stdout.write('\n');
|
|
242
|
+
});
|
|
243
|
+
// orch schedule delete <schedule-id>
|
|
244
|
+
schedule
|
|
245
|
+
.command('delete <schedule-id>')
|
|
246
|
+
.description('Delete a schedule')
|
|
247
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
248
|
+
.action(async (scheduleId, options) => {
|
|
249
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
250
|
+
if (!config.apiKey) {
|
|
251
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
252
|
+
}
|
|
253
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
254
|
+
await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
|
|
255
|
+
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule ${scheduleId} deleted\n`);
|
|
256
|
+
});
|
|
257
|
+
// orch schedule trigger <schedule-id>
|
|
258
|
+
schedule
|
|
259
|
+
.command('trigger <schedule-id>')
|
|
260
|
+
.description('Manually trigger a schedule execution')
|
|
261
|
+
.option('--input <json>', 'Override input data as JSON')
|
|
262
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
263
|
+
.action(async (scheduleId, options) => {
|
|
264
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
265
|
+
if (!config.apiKey) {
|
|
266
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
267
|
+
}
|
|
268
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
269
|
+
let body;
|
|
270
|
+
if (options.input) {
|
|
271
|
+
try {
|
|
272
|
+
JSON.parse(options.input); // validate
|
|
273
|
+
body = options.input;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
throw new errors_1.CliError('Invalid JSON in --input');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
process.stdout.write('Triggering schedule...\n');
|
|
280
|
+
const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/trigger`, body ? {
|
|
281
|
+
body,
|
|
282
|
+
headers: { 'Content-Type': 'application/json' },
|
|
283
|
+
} : {});
|
|
284
|
+
process.stdout.write(chalk_1.default.green('\u2713') + ` Run completed\n\n`);
|
|
285
|
+
process.stdout.write(` Run ID: ${result.run_id}\n`);
|
|
286
|
+
process.stdout.write(` Status: ${statusColor(result.status)}\n`);
|
|
287
|
+
process.stdout.write(` Duration: ${result.duration_ms}ms\n`);
|
|
288
|
+
if (result.error) {
|
|
289
|
+
process.stdout.write(` Error: ${chalk_1.default.red(result.error)}\n`);
|
|
290
|
+
}
|
|
291
|
+
if (result.output) {
|
|
292
|
+
process.stdout.write(`\n Output:\n`);
|
|
293
|
+
process.stdout.write(` ${JSON.stringify(result.output, null, 2).split('\n').join('\n ')}\n`);
|
|
294
|
+
}
|
|
295
|
+
process.stdout.write('\n');
|
|
296
|
+
});
|
|
297
|
+
}
|
package/package.json
CHANGED