@orchagent/cli 0.3.52 → 0.3.54

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.
@@ -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_name', 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.52",
3
+ "version": "0.3.54",
4
4
  "description": "Command-line interface for orchagent — deploy and run AI agents for your team",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",