@orchagent/cli 0.3.90 → 0.3.92
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/completion.js +379 -0
- package/dist/commands/dag.js +16 -7
- package/dist/commands/delete.js +9 -4
- package/dist/commands/diff-format.js +300 -0
- package/dist/commands/diff.js +12 -131
- package/dist/commands/estimate.js +5 -2
- package/dist/commands/fork.js +7 -1
- package/dist/commands/health.js +90 -7
- package/dist/commands/index.js +6 -0
- package/dist/commands/info.js +8 -1
- package/dist/commands/init-wizard.js +225 -0
- package/dist/commands/init.js +109 -3
- package/dist/commands/login.js +8 -0
- package/dist/commands/logs.js +17 -7
- package/dist/commands/metrics.js +16 -7
- package/dist/commands/publish.js +74 -66
- package/dist/commands/replay.js +16 -7
- package/dist/commands/run.js +158 -33
- package/dist/commands/scaffold.js +213 -0
- package/dist/commands/schedule.js +112 -11
- package/dist/commands/secrets.js +16 -7
- package/dist/commands/service.js +16 -7
- package/dist/commands/skill.js +84 -8
- package/dist/commands/templates/cron-job.js +259 -0
- package/dist/commands/trace.js +16 -7
- package/dist/commands/tree.js +7 -1
- package/dist/commands/update.js +46 -9
- package/dist/commands/validate.js +264 -0
- package/dist/commands/workspace.js +16 -7
- package/dist/lib/agent-ref.js +4 -1
- package/dist/lib/browser-auth.js +1 -0
- package/dist/lib/scaffold-orchestration.js +237 -0
- package/dist/lib/validate.js +478 -0
- package/package.json +1 -1
package/dist/commands/info.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const config_1 = require("../lib/config");
|
|
9
9
|
const api_1 = require("../lib/api");
|
|
10
10
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
11
|
+
const errors_1 = require("../lib/errors");
|
|
11
12
|
function formatSchema(schema, indent = ' ') {
|
|
12
13
|
const lines = [];
|
|
13
14
|
const props = schema.properties || {};
|
|
@@ -147,7 +148,13 @@ function registerInfoCommand(program) {
|
|
|
147
148
|
.option('--json', 'Output as JSON')
|
|
148
149
|
.action(async (agentArg, options) => {
|
|
149
150
|
const config = await (0, config_1.getResolvedConfig)();
|
|
150
|
-
const
|
|
151
|
+
const parsed = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
152
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
153
|
+
const org = parsed.org ?? configFile.workspace ?? config.defaultOrg;
|
|
154
|
+
if (!org) {
|
|
155
|
+
throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
|
|
156
|
+
}
|
|
157
|
+
const { agent, version } = parsed;
|
|
151
158
|
// Resolve workspace context for the target org
|
|
152
159
|
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
153
160
|
// Fetch agent metadata
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive wizard for `orch init`.
|
|
4
|
+
*
|
|
5
|
+
* Runs when `orch init` is invoked without arguments in a TTY.
|
|
6
|
+
* Uses Node.js built-in readline/promises — no extra dependencies.
|
|
7
|
+
*
|
|
8
|
+
* The wizard leads with "What do you want to build?" to directly address
|
|
9
|
+
* the most common new-user friction — not knowing which type/flavor to pick.
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TEMPLATE_REGISTRY = void 0;
|
|
16
|
+
exports.runInitWizard = runInitWizard;
|
|
17
|
+
exports.printTemplateList = printTemplateList;
|
|
18
|
+
const promises_1 = __importDefault(require("readline/promises"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
exports.TEMPLATE_REGISTRY = [
|
|
21
|
+
{ name: 'cron-job', description: 'Scheduled task — daily reports, syncs, cleanups', type: 'tool', language: 'both', runMode: 'on_demand' },
|
|
22
|
+
{ name: 'discord', description: 'Discord bot powered by Claude (Python)', type: 'agent', language: 'python', runMode: 'always_on' },
|
|
23
|
+
{ name: 'discord-js', description: 'Discord bot powered by Claude (JavaScript)', type: 'agent', language: 'javascript', runMode: 'always_on' },
|
|
24
|
+
{ name: 'support-agent', description: 'Multi-platform support agent (Discord/Telegram/Slack)', type: 'agent', language: 'python', runMode: 'always_on' },
|
|
25
|
+
{ name: 'fan-out', description: 'Parallel orchestration — call agents concurrently', type: 'agent', language: 'both', runMode: 'on_demand' },
|
|
26
|
+
{ name: 'pipeline', description: 'Sequential orchestration — chain agents in series', type: 'agent', language: 'both', runMode: 'on_demand' },
|
|
27
|
+
{ name: 'map-reduce', description: 'Map-reduce orchestration — split, process, aggregate', type: 'agent', language: 'both', runMode: 'on_demand' },
|
|
28
|
+
{ name: 'github-weekly-summary', description: 'GitHub activity analyser with Discord delivery', type: 'agent', language: 'python', runMode: 'on_demand' },
|
|
29
|
+
];
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Prompt helpers (readline-based, no dependencies)
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
function createInterface() {
|
|
34
|
+
return promises_1.default.createInterface({
|
|
35
|
+
input: process.stdin,
|
|
36
|
+
output: process.stderr, // prompts on stderr so stdout stays clean for piping
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function promptText(rl, question, defaultValue) {
|
|
40
|
+
const suffix = defaultValue ? ` (${defaultValue})` : '';
|
|
41
|
+
const answer = await rl.question(` ${question}${suffix}: `);
|
|
42
|
+
return answer.trim() || defaultValue || '';
|
|
43
|
+
}
|
|
44
|
+
async function promptSelect(rl, question, options) {
|
|
45
|
+
process.stderr.write(`\n ${question}\n`);
|
|
46
|
+
for (let i = 0; i < options.length; i++) {
|
|
47
|
+
const opt = options[i];
|
|
48
|
+
process.stderr.write(` ${i + 1}) ${opt.label} — ${opt.description}\n`);
|
|
49
|
+
}
|
|
50
|
+
process.stderr.write('\n');
|
|
51
|
+
while (true) {
|
|
52
|
+
const answer = await rl.question(` Choice [1-${options.length}]: `);
|
|
53
|
+
const num = parseInt(answer.trim(), 10);
|
|
54
|
+
if (num >= 1 && num <= options.length) {
|
|
55
|
+
return options[num - 1].value;
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write(` Please enter a number between 1 and ${options.length}.\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Language follow-up (shared by several use cases)
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
async function promptLanguage(rl) {
|
|
64
|
+
return promptSelect(rl, 'Language?', [
|
|
65
|
+
{ value: 'python', label: 'Python', description: 'Recommended — broadest library support' },
|
|
66
|
+
{ value: 'javascript', label: 'JavaScript', description: 'Node.js runtime' },
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// "More templates" sub-flow
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
async function handleMoreTemplates(rl, nameForResult) {
|
|
73
|
+
const language = await promptLanguage(rl);
|
|
74
|
+
const applicableTemplates = exports.TEMPLATE_REGISTRY.filter(t => t.language === 'both' || t.language === language);
|
|
75
|
+
const templateOptions = [
|
|
76
|
+
{ value: 'none', label: 'No template', description: 'Start from scratch (choose type manually)' },
|
|
77
|
+
...applicableTemplates.map(t => ({
|
|
78
|
+
value: t.name,
|
|
79
|
+
label: t.name,
|
|
80
|
+
description: t.description,
|
|
81
|
+
})),
|
|
82
|
+
];
|
|
83
|
+
const template = await promptSelect(rl, 'Pick a template:', templateOptions);
|
|
84
|
+
if (template !== 'none') {
|
|
85
|
+
const info = exports.TEMPLATE_REGISTRY.find(t => t.name === template);
|
|
86
|
+
rl.close();
|
|
87
|
+
return {
|
|
88
|
+
name: nameForResult,
|
|
89
|
+
type: info.type,
|
|
90
|
+
language,
|
|
91
|
+
template,
|
|
92
|
+
runMode: info.runMode,
|
|
93
|
+
orchestrator: false,
|
|
94
|
+
loop: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// No template — fall back to manual type selection
|
|
98
|
+
const type = await promptSelect(rl, 'Agent type?', [
|
|
99
|
+
{ value: 'prompt', label: 'prompt', description: 'Single LLM call with structured I/O' },
|
|
100
|
+
{ value: 'tool', label: 'tool', description: 'Your own code (API calls, file processing)' },
|
|
101
|
+
{ value: 'agent', label: 'agent', description: 'Multi-step LLM reasoning with tool use' },
|
|
102
|
+
]);
|
|
103
|
+
let runMode = 'on_demand';
|
|
104
|
+
let orchestrator = false;
|
|
105
|
+
let loop = false;
|
|
106
|
+
if (type === 'tool') {
|
|
107
|
+
runMode = await promptSelect(rl, 'Run mode?', [
|
|
108
|
+
{ value: 'on_demand', label: 'on_demand', description: 'Run per invocation (default)' },
|
|
109
|
+
{ value: 'always_on', label: 'always_on', description: 'Long-lived HTTP service' },
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
if (type === 'agent') {
|
|
113
|
+
runMode = await promptSelect(rl, 'Run mode?', [
|
|
114
|
+
{ value: 'on_demand', label: 'on_demand', description: 'Run per invocation (default)' },
|
|
115
|
+
{ value: 'always_on', label: 'always_on', description: 'Long-lived HTTP service' },
|
|
116
|
+
]);
|
|
117
|
+
const subtypeOptions = [
|
|
118
|
+
{ value: 'code', label: 'Code runtime', description: 'You write the logic (call any LLM provider)' },
|
|
119
|
+
{ value: 'orchestrator', label: 'Orchestrator', description: 'Coordinate other agents via SDK' },
|
|
120
|
+
];
|
|
121
|
+
if (language === 'python') {
|
|
122
|
+
subtypeOptions.push({ value: 'loop', label: 'Managed loop', description: 'Platform-managed LLM loop with tool use' });
|
|
123
|
+
}
|
|
124
|
+
const agentSubtype = await promptSelect(rl, 'Agent execution mode?', subtypeOptions);
|
|
125
|
+
orchestrator = agentSubtype === 'orchestrator';
|
|
126
|
+
loop = agentSubtype === 'loop';
|
|
127
|
+
}
|
|
128
|
+
rl.close();
|
|
129
|
+
return { name: nameForResult, type, language, template: undefined, runMode, orchestrator, loop };
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Wizard
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
async function runInitWizard() {
|
|
135
|
+
const rl = createInterface();
|
|
136
|
+
try {
|
|
137
|
+
process.stderr.write('\n orch init — interactive setup\n');
|
|
138
|
+
process.stderr.write(' ─────────────────────────────\n\n');
|
|
139
|
+
// 1. Agent name
|
|
140
|
+
const dirName = path_1.default.basename(process.cwd());
|
|
141
|
+
const name = await promptText(rl, 'Agent name', dirName);
|
|
142
|
+
const nameForResult = name !== dirName ? name : undefined;
|
|
143
|
+
// 2. What do you want to build? (use-case-driven — replaces type/template questions)
|
|
144
|
+
const useCase = await promptSelect(rl, 'What do you want to build?', [
|
|
145
|
+
{ value: 'prompt', label: 'Prompt agent', description: 'Single LLM call with structured I/O (simplest)' },
|
|
146
|
+
{ value: 'tool-py', label: 'Tool (Python)', description: 'Your code processes data (stdin/stdout JSON)' },
|
|
147
|
+
{ value: 'tool-js', label: 'Tool (JavaScript)', description: 'Your code processes data (Node.js)' },
|
|
148
|
+
{ value: 'cron-job', label: 'Scheduled job', description: 'Runs on a cron schedule (reports, syncs, cleanups)' },
|
|
149
|
+
{ value: 'discord-bot', label: 'Discord bot', description: 'Always-on chatbot in Discord channels' },
|
|
150
|
+
{ value: 'orchestrator', label: 'Orchestrator', description: 'Coordinate multiple agents via SDK' },
|
|
151
|
+
{ value: 'agent-loop', label: 'AI agent (LLM loop)', description: 'Multi-step reasoning with tool use' },
|
|
152
|
+
{ value: 'skill', label: 'Knowledge skill', description: 'Reusable knowledge module for other agents' },
|
|
153
|
+
{ value: 'more', label: 'More templates...', description: 'Fan-out, pipeline, map-reduce, support agent, etc.' },
|
|
154
|
+
]);
|
|
155
|
+
// --- Direct-resolution use cases (no follow-up needed) ---
|
|
156
|
+
if (useCase === 'prompt') {
|
|
157
|
+
rl.close();
|
|
158
|
+
return { name: nameForResult, type: 'prompt', language: 'python', template: undefined, runMode: 'on_demand', orchestrator: false, loop: false };
|
|
159
|
+
}
|
|
160
|
+
if (useCase === 'skill') {
|
|
161
|
+
rl.close();
|
|
162
|
+
return { name: nameForResult, type: 'skill', language: 'python', template: undefined, runMode: 'on_demand', orchestrator: false, loop: false };
|
|
163
|
+
}
|
|
164
|
+
if (useCase === 'tool-py') {
|
|
165
|
+
rl.close();
|
|
166
|
+
return { name: nameForResult, type: 'tool', language: 'python', template: undefined, runMode: 'on_demand', orchestrator: false, loop: false };
|
|
167
|
+
}
|
|
168
|
+
if (useCase === 'tool-js') {
|
|
169
|
+
rl.close();
|
|
170
|
+
return { name: nameForResult, type: 'tool', language: 'javascript', template: undefined, runMode: 'on_demand', orchestrator: false, loop: false };
|
|
171
|
+
}
|
|
172
|
+
if (useCase === 'agent-loop') {
|
|
173
|
+
rl.close();
|
|
174
|
+
return { name: nameForResult, type: 'agent', language: 'python', template: undefined, runMode: 'on_demand', orchestrator: false, loop: true };
|
|
175
|
+
}
|
|
176
|
+
// --- Use cases that need a language follow-up ---
|
|
177
|
+
if (useCase === 'cron-job') {
|
|
178
|
+
const lang = await promptLanguage(rl);
|
|
179
|
+
rl.close();
|
|
180
|
+
return { name: nameForResult, type: 'tool', language: lang, template: 'cron-job', runMode: 'on_demand', orchestrator: false, loop: false };
|
|
181
|
+
}
|
|
182
|
+
if (useCase === 'discord-bot') {
|
|
183
|
+
const lang = await promptSelect(rl, 'Language?', [
|
|
184
|
+
{ value: 'python', label: 'Python', description: 'Recommended — discord.py + anthropic' },
|
|
185
|
+
{ value: 'javascript', label: 'JavaScript', description: 'discord.js + @anthropic-ai/sdk' },
|
|
186
|
+
]);
|
|
187
|
+
const template = lang === 'javascript' ? 'discord-js' : 'discord';
|
|
188
|
+
rl.close();
|
|
189
|
+
return { name: nameForResult, type: 'agent', language: lang, template, runMode: 'always_on', orchestrator: false, loop: false };
|
|
190
|
+
}
|
|
191
|
+
if (useCase === 'orchestrator') {
|
|
192
|
+
const lang = await promptLanguage(rl);
|
|
193
|
+
rl.close();
|
|
194
|
+
return { name: nameForResult, type: 'agent', language: lang, template: undefined, runMode: 'on_demand', orchestrator: true, loop: false };
|
|
195
|
+
}
|
|
196
|
+
// --- "More templates..." sub-flow ---
|
|
197
|
+
if (useCase === 'more') {
|
|
198
|
+
return handleMoreTemplates(rl, nameForResult);
|
|
199
|
+
}
|
|
200
|
+
// Shouldn't reach here, but handle gracefully
|
|
201
|
+
rl.close();
|
|
202
|
+
return { name: nameForResult, type: 'prompt', language: 'python', template: undefined, runMode: 'on_demand', orchestrator: false, loop: false };
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
rl.close();
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// List templates (for --list-templates flag)
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
function printTemplateList() {
|
|
213
|
+
process.stdout.write('\nAvailable templates:\n\n');
|
|
214
|
+
const nameWidth = Math.max(...exports.TEMPLATE_REGISTRY.map(t => t.name.length)) + 2;
|
|
215
|
+
const langWidth = 12;
|
|
216
|
+
process.stdout.write(` ${'TEMPLATE'.padEnd(nameWidth)}${'LANGUAGE'.padEnd(langWidth)}${'RUN MODE'.padEnd(12)}DESCRIPTION\n`);
|
|
217
|
+
process.stdout.write(` ${'─'.repeat(nameWidth)}${'─'.repeat(langWidth)}${'─'.repeat(12)}${'─'.repeat(40)}\n`);
|
|
218
|
+
for (const t of exports.TEMPLATE_REGISTRY) {
|
|
219
|
+
const lang = t.language === 'both' ? 'py / js' : t.language === 'python' ? 'python' : 'javascript';
|
|
220
|
+
process.stdout.write(` ${t.name.padEnd(nameWidth)}${lang.padEnd(langWidth)}${t.runMode.padEnd(12)}${t.description}\n`);
|
|
221
|
+
}
|
|
222
|
+
process.stdout.write('\nUsage:\n');
|
|
223
|
+
process.stdout.write(' orch init my-agent --template <name>\n');
|
|
224
|
+
process.stdout.write(' orch init my-agent --template <name> --language javascript\n\n');
|
|
225
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -4,10 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.registerInitCommand = registerInitCommand;
|
|
7
|
+
const commander_1 = require("commander");
|
|
7
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const errors_1 = require("../lib/errors");
|
|
11
|
+
const init_wizard_1 = require("./init-wizard");
|
|
10
12
|
const github_weekly_summary_1 = require("./templates/github-weekly-summary");
|
|
13
|
+
const cron_job_1 = require("./templates/cron-job");
|
|
11
14
|
const support_agent_1 = require("./templates/support-agent");
|
|
12
15
|
const MANIFEST_TEMPLATE = `{
|
|
13
16
|
"name": "my-agent",
|
|
@@ -1498,15 +1501,49 @@ function resolveInitFlavor(typeOption) {
|
|
|
1498
1501
|
function registerInitCommand(program) {
|
|
1499
1502
|
program
|
|
1500
1503
|
.command('init')
|
|
1501
|
-
.description('Initialize a new agent project')
|
|
1504
|
+
.description('Initialize a new agent project (interactive wizard when called without arguments)')
|
|
1502
1505
|
.argument('[name]', 'Agent name (default: current directory name)')
|
|
1503
1506
|
.option('--type <type>', 'Type: prompt, tool, agent, or skill (legacy aliases: agentic, code)', 'prompt')
|
|
1504
1507
|
.option('--orchestrator', 'Create an orchestrator agent with dependency scaffolding and SDK boilerplate')
|
|
1505
1508
|
.option('--run-mode <mode>', 'Run mode for agents: on_demand or always_on', 'on_demand')
|
|
1506
1509
|
.option('--language <lang>', 'Language: python or javascript (default: python)', 'python')
|
|
1507
1510
|
.option('--loop', 'Use platform-managed LLM loop instead of code runtime (requires --type agent)')
|
|
1508
|
-
.option('--template <name>', 'Start from a template (
|
|
1511
|
+
.option('--template <name>', 'Start from a template (use --list-templates to see options)')
|
|
1512
|
+
.option('--list-templates', 'Show available templates with descriptions')
|
|
1509
1513
|
.action(async (name, options) => {
|
|
1514
|
+
// --list-templates: print and exit
|
|
1515
|
+
if (options.listTemplates) {
|
|
1516
|
+
(0, init_wizard_1.printTemplateList)();
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
// Interactive wizard: no name, TTY, and no explicit flags that indicate non-interactive intent
|
|
1520
|
+
const rawArgs = process.argv.slice(2);
|
|
1521
|
+
const initArgIndex = rawArgs.indexOf('init');
|
|
1522
|
+
const argsAfterInit = initArgIndex >= 0 ? rawArgs.slice(initArgIndex + 1) : [];
|
|
1523
|
+
const hasExplicitFlags = argsAfterInit.some(a => a.startsWith('--'));
|
|
1524
|
+
const hasNameArg = name !== undefined;
|
|
1525
|
+
if (!hasNameArg && !hasExplicitFlags && process.stdin.isTTY) {
|
|
1526
|
+
const wizard = await (0, init_wizard_1.runInitWizard)();
|
|
1527
|
+
// Re-invoke the action with wizard results by constructing args
|
|
1528
|
+
const wizardArgs = ['node', 'orch', 'init'];
|
|
1529
|
+
if (wizard.name)
|
|
1530
|
+
wizardArgs.push(wizard.name);
|
|
1531
|
+
wizardArgs.push('--type', wizard.type);
|
|
1532
|
+
wizardArgs.push('--language', wizard.language);
|
|
1533
|
+
wizardArgs.push('--run-mode', wizard.runMode);
|
|
1534
|
+
if (wizard.template)
|
|
1535
|
+
wizardArgs.push('--template', wizard.template);
|
|
1536
|
+
if (wizard.orchestrator)
|
|
1537
|
+
wizardArgs.push('--orchestrator');
|
|
1538
|
+
if (wizard.loop)
|
|
1539
|
+
wizardArgs.push('--loop');
|
|
1540
|
+
// Create a fresh program to run with wizard args
|
|
1541
|
+
const wizardProgram = new commander_1.Command();
|
|
1542
|
+
wizardProgram.exitOverride();
|
|
1543
|
+
registerInitCommand(wizardProgram);
|
|
1544
|
+
await wizardProgram.parseAsync(wizardArgs);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1510
1547
|
const cwd = process.cwd();
|
|
1511
1548
|
let runMode = (options.runMode || 'on_demand').trim().toLowerCase();
|
|
1512
1549
|
if (!['on_demand', 'always_on'].includes(runMode)) {
|
|
@@ -1530,7 +1567,7 @@ function registerInitCommand(program) {
|
|
|
1530
1567
|
}
|
|
1531
1568
|
if (options.template) {
|
|
1532
1569
|
const template = options.template.trim().toLowerCase();
|
|
1533
|
-
const validTemplates = ['fan-out', 'pipeline', 'map-reduce', 'support-agent', 'discord', 'discord-js', 'github-weekly-summary'];
|
|
1570
|
+
const validTemplates = ['fan-out', 'pipeline', 'map-reduce', 'support-agent', 'discord', 'discord-js', 'github-weekly-summary', 'cron-job'];
|
|
1534
1571
|
if (!validTemplates.includes(template)) {
|
|
1535
1572
|
throw new errors_1.CliError(`Unknown --template '${template}'. Available templates: ${validTemplates.join(', ')}`);
|
|
1536
1573
|
}
|
|
@@ -1564,6 +1601,9 @@ function registerInitCommand(program) {
|
|
|
1564
1601
|
else if (template === 'github-weekly-summary') {
|
|
1565
1602
|
initMode = { type: 'agent', flavor: 'github_weekly_summary' };
|
|
1566
1603
|
}
|
|
1604
|
+
else if (template === 'cron-job') {
|
|
1605
|
+
initMode = { type: 'tool', flavor: 'cron_job' };
|
|
1606
|
+
}
|
|
1567
1607
|
}
|
|
1568
1608
|
// Validate --language option
|
|
1569
1609
|
const language = (options.language || 'python').trim().toLowerCase();
|
|
@@ -1913,6 +1953,72 @@ function registerInitCommand(program) {
|
|
|
1913
1953
|
process.stdout.write(AGENT_BUILDER_HINT);
|
|
1914
1954
|
return;
|
|
1915
1955
|
}
|
|
1956
|
+
// Handle cron-job template
|
|
1957
|
+
if (initMode.flavor === 'cron_job') {
|
|
1958
|
+
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
1959
|
+
try {
|
|
1960
|
+
await promises_1.default.access(manifestPath);
|
|
1961
|
+
throw new errors_1.CliError(`Already initialized (orchagent.json exists in ${name ? name + '/' : 'current directory'})`);
|
|
1962
|
+
}
|
|
1963
|
+
catch (err) {
|
|
1964
|
+
if (err.code !== 'ENOENT') {
|
|
1965
|
+
throw err;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
const manifest = {
|
|
1969
|
+
name: agentName,
|
|
1970
|
+
type: 'tool',
|
|
1971
|
+
description: 'A scheduled job that runs on a cron schedule',
|
|
1972
|
+
run_mode: 'on_demand',
|
|
1973
|
+
runtime: { command: isJavaScript ? 'node main.js' : 'python main.py' },
|
|
1974
|
+
required_secrets: [],
|
|
1975
|
+
tags: ['scheduled', 'cron'],
|
|
1976
|
+
};
|
|
1977
|
+
if (isJavaScript) {
|
|
1978
|
+
manifest.entrypoint = 'main.js';
|
|
1979
|
+
}
|
|
1980
|
+
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
1981
|
+
if (isJavaScript) {
|
|
1982
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), cron_job_1.CRON_JOB_MAIN_JS);
|
|
1983
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), JSON.stringify({
|
|
1984
|
+
name: agentName,
|
|
1985
|
+
private: true,
|
|
1986
|
+
type: 'commonjs',
|
|
1987
|
+
dependencies: {},
|
|
1988
|
+
}, null, 2) + '\n');
|
|
1989
|
+
}
|
|
1990
|
+
else {
|
|
1991
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), cron_job_1.CRON_JOB_MAIN_PY);
|
|
1992
|
+
}
|
|
1993
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'schema.json'), cron_job_1.CRON_JOB_SCHEMA);
|
|
1994
|
+
await promises_1.default.writeFile(path_1.default.join(targetDir, 'README.md'), (0, cron_job_1.cronJobReadme)(agentName));
|
|
1995
|
+
const prefix = name ? name + '/' : '';
|
|
1996
|
+
process.stdout.write(`Initialized scheduled job "${agentName}" in ${targetDir}\n`);
|
|
1997
|
+
process.stdout.write(`\nFiles created:\n`);
|
|
1998
|
+
process.stdout.write(` ${prefix}orchagent.json - Agent configuration (cron job)\n`);
|
|
1999
|
+
if (isJavaScript) {
|
|
2000
|
+
process.stdout.write(` ${prefix}main.js - Scheduled job entrypoint\n`);
|
|
2001
|
+
process.stdout.write(` ${prefix}package.json - npm dependencies\n`);
|
|
2002
|
+
}
|
|
2003
|
+
else {
|
|
2004
|
+
process.stdout.write(` ${prefix}main.py - Scheduled job entrypoint\n`);
|
|
2005
|
+
}
|
|
2006
|
+
process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
|
|
2007
|
+
process.stdout.write(` ${prefix}README.md - Setup guide with cron patterns\n`);
|
|
2008
|
+
process.stdout.write(`\nNext steps:\n`);
|
|
2009
|
+
const stepNum = name ? 2 : 1;
|
|
2010
|
+
if (name) {
|
|
2011
|
+
process.stdout.write(` 1. cd ${name}\n`);
|
|
2012
|
+
}
|
|
2013
|
+
const mainFile = isJavaScript ? 'main.js' : 'main.py';
|
|
2014
|
+
const testCmd = isJavaScript ? 'node main.js' : 'python main.py';
|
|
2015
|
+
process.stdout.write(` ${stepNum}. Edit ${mainFile} with your job logic\n`);
|
|
2016
|
+
process.stdout.write(` ${stepNum + 1}. Test: echo '{}' | ${testCmd}\n`);
|
|
2017
|
+
process.stdout.write(` ${stepNum + 2}. Publish: orch publish\n`);
|
|
2018
|
+
process.stdout.write(` ${stepNum + 3}. Schedule: orch schedule create <org>/${agentName} --cron "0 9 * * 1"\n`);
|
|
2019
|
+
process.stdout.write(AGENT_BUILDER_HINT);
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
1916
2022
|
const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
|
|
1917
2023
|
const promptPath = path_1.default.join(targetDir, 'prompt.md');
|
|
1918
2024
|
const schemaPath = path_1.default.join(targetDir, 'schema.json');
|
package/dist/commands/login.js
CHANGED
|
@@ -56,6 +56,7 @@ async function keyBasedLogin(apiKey) {
|
|
|
56
56
|
const resolved = await (0, config_1.getResolvedConfig)({ api_key: apiKey });
|
|
57
57
|
const org = await (0, api_1.getOrg)(resolved);
|
|
58
58
|
const existing = await (0, config_1.loadConfig)();
|
|
59
|
+
const isFirstLogin = !existing.api_key;
|
|
59
60
|
const nextConfig = {
|
|
60
61
|
...existing,
|
|
61
62
|
api_key: apiKey,
|
|
@@ -67,12 +68,16 @@ async function keyBasedLogin(apiKey) {
|
|
|
67
68
|
await (0, config_1.saveConfig)(nextConfig);
|
|
68
69
|
await (0, analytics_1.track)('cli_login', { method: 'key' });
|
|
69
70
|
process.stdout.write(`✓ Logged in to ${org.slug}\n`);
|
|
71
|
+
if (isFirstLogin) {
|
|
72
|
+
process.stdout.write('\n Tip: Run `orch doctor` to verify your setup.\n\n');
|
|
73
|
+
}
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
76
|
* Login via browser OAuth flow.
|
|
73
77
|
*/
|
|
74
78
|
async function browserBasedLogin(port) {
|
|
75
79
|
const existing = await (0, config_1.loadConfig)();
|
|
80
|
+
const isFirstLogin = !existing.api_key;
|
|
76
81
|
const resolved = await (0, config_1.getResolvedConfig)();
|
|
77
82
|
process.stdout.write('Opening browser for authentication...\n');
|
|
78
83
|
try {
|
|
@@ -88,6 +93,9 @@ async function browserBasedLogin(port) {
|
|
|
88
93
|
await (0, config_1.saveConfig)(nextConfig);
|
|
89
94
|
await (0, analytics_1.track)('cli_login', { method: 'browser' });
|
|
90
95
|
process.stdout.write(`\n✓ Logged in to ${result.orgSlug}\n`);
|
|
96
|
+
if (isFirstLogin) {
|
|
97
|
+
process.stdout.write('\n Tip: Run `orch doctor` to verify your setup.\n\n');
|
|
98
|
+
}
|
|
91
99
|
}
|
|
92
100
|
catch (err) {
|
|
93
101
|
if (err instanceof errors_1.CliError) {
|
package/dist/commands/logs.js
CHANGED
|
@@ -16,15 +16,25 @@ const output_1 = require("../lib/output");
|
|
|
16
16
|
async function resolveWorkspaceId(config, slug) {
|
|
17
17
|
const configFile = await (0, config_1.loadConfig)();
|
|
18
18
|
const targetSlug = slug ?? configFile.workspace;
|
|
19
|
-
if (!targetSlug) {
|
|
20
|
-
throw new errors_1.CliError('No workspace specified. Use --workspace <slug> or run `orch workspace use <slug>` first.');
|
|
21
|
-
}
|
|
22
19
|
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
if (targetSlug) {
|
|
21
|
+
const workspace = response.workspaces.find((w) => w.slug === targetSlug);
|
|
22
|
+
if (!workspace) {
|
|
23
|
+
throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
|
|
24
|
+
}
|
|
25
|
+
return workspace.id;
|
|
26
|
+
}
|
|
27
|
+
// No workspace specified — auto-select if user has exactly one
|
|
28
|
+
if (response.workspaces.length === 0) {
|
|
29
|
+
throw new errors_1.CliError('No workspaces found. Create one with `orch workspace create <name>`.');
|
|
30
|
+
}
|
|
31
|
+
if (response.workspaces.length === 1) {
|
|
32
|
+
return response.workspaces[0].id;
|
|
26
33
|
}
|
|
27
|
-
|
|
34
|
+
// Multiple workspaces — list them and ask the user to pick
|
|
35
|
+
const slugs = response.workspaces.map((w) => w.slug).join(', ');
|
|
36
|
+
throw new errors_1.CliError(`Multiple workspaces available: ${slugs}\n` +
|
|
37
|
+
'Specify one with --workspace <slug> or run `orch workspace use <slug>`.');
|
|
28
38
|
}
|
|
29
39
|
function formatDate(iso) {
|
|
30
40
|
if (!iso)
|
package/dist/commands/metrics.js
CHANGED
|
@@ -15,15 +15,24 @@ const output_1 = require("../lib/output");
|
|
|
15
15
|
async function resolveWorkspaceId(config, slug) {
|
|
16
16
|
const configFile = await (0, config_1.loadConfig)();
|
|
17
17
|
const targetSlug = slug ?? configFile.workspace;
|
|
18
|
-
if (!targetSlug) {
|
|
19
|
-
throw new errors_1.CliError('No workspace specified. Use --workspace <slug> or run `orch workspace use <slug>` first.');
|
|
20
|
-
}
|
|
21
18
|
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
if (targetSlug) {
|
|
20
|
+
const workspace = response.workspaces.find((w) => w.slug === targetSlug);
|
|
21
|
+
if (!workspace) {
|
|
22
|
+
throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
|
|
23
|
+
}
|
|
24
|
+
return workspace.id;
|
|
25
|
+
}
|
|
26
|
+
// No workspace specified — auto-select if user has exactly one
|
|
27
|
+
if (response.workspaces.length === 0) {
|
|
28
|
+
throw new errors_1.CliError('No workspaces found. Create one with `orch workspace create <name>`.');
|
|
29
|
+
}
|
|
30
|
+
if (response.workspaces.length === 1) {
|
|
31
|
+
return response.workspaces[0].id;
|
|
25
32
|
}
|
|
26
|
-
|
|
33
|
+
const slugs = response.workspaces.map((w) => w.slug).join(', ');
|
|
34
|
+
throw new errors_1.CliError(`Multiple workspaces available: ${slugs}\n` +
|
|
35
|
+
'Specify one with --workspace <slug> or run `orch workspace use <slug>`.');
|
|
27
36
|
}
|
|
28
37
|
function formatDuration(ms) {
|
|
29
38
|
if (ms === 0)
|