@orchagent/cli 0.3.91 → 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.
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ /**
3
+ * Cron-job template for `orch init --template cron-job`.
4
+ *
5
+ * Provides Python and JavaScript scaffolding for scheduled tasks —
6
+ * daily reports, data syncs, cleanups, notifications, etc.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CRON_JOB_SCHEMA = exports.CRON_JOB_MAIN_JS = exports.CRON_JOB_MAIN_PY = void 0;
10
+ exports.cronJobReadme = cronJobReadme;
11
+ // ---------------------------------------------------------------------------
12
+ // Python template
13
+ // ---------------------------------------------------------------------------
14
+ exports.CRON_JOB_MAIN_PY = `"""
15
+ orchagent scheduled job.
16
+
17
+ Runs on a cron schedule to perform periodic tasks.
18
+
19
+ Schedule examples:
20
+ orch schedule create org/my-job --cron "0 9 * * 1" # Every Monday 9 AM UTC
21
+ orch schedule create org/my-job --cron "0 0 * * *" # Daily at midnight
22
+ orch schedule create org/my-job --cron "0 */6 * * *" # Every 6 hours
23
+
24
+ Local test:
25
+ echo '{}' | python main.py
26
+ """
27
+
28
+ import json
29
+ import os
30
+ import sys
31
+ from datetime import datetime, timezone
32
+
33
+
34
+ def main():
35
+ raw = sys.stdin.read()
36
+ try:
37
+ data = json.loads(raw) if raw.strip() else {}
38
+ except json.JSONDecodeError:
39
+ print(json.dumps({"error": "Invalid JSON input"}))
40
+ sys.exit(1)
41
+
42
+ dry_run = data.get("options", {}).get("dry_run", False)
43
+
44
+ # --- Your scheduled job logic here ---
45
+ # Common patterns:
46
+ # - Fetch data from an API and store results
47
+ # - Generate a daily/weekly report
48
+ # - Clean up old records or files
49
+ # - Send digest notifications (email, Slack, Discord)
50
+ #
51
+ # To use workspace secrets (API keys, webhook URLs):
52
+ # 1. Add to "required_secrets" in orchagent.json
53
+ # 2. Set in workspace: orch secrets set MY_SECRET <value>
54
+ # 3. Access via: os.environ["MY_SECRET"]
55
+ #
56
+ # Example: send to a webhook
57
+ # import urllib.request
58
+ # webhook_url = os.environ["WEBHOOK_URL"]
59
+ # req = urllib.request.Request(webhook_url, data=json.dumps(payload).encode(),
60
+ # headers={"Content-Type": "application/json"})
61
+ # urllib.request.urlopen(req)
62
+
63
+ now = datetime.now(timezone.utc).isoformat()
64
+
65
+ report = {
66
+ "generated_at": now,
67
+ "status": "completed" if not dry_run else "dry_run",
68
+ "summary": "Scheduled job ran successfully",
69
+ "items_processed": 0,
70
+ }
71
+ # --- End your logic ---
72
+
73
+ print(json.dumps({
74
+ "result": report,
75
+ "success": True,
76
+ }))
77
+
78
+
79
+ if __name__ == "__main__":
80
+ main()
81
+ `;
82
+ // ---------------------------------------------------------------------------
83
+ // JavaScript template
84
+ // ---------------------------------------------------------------------------
85
+ exports.CRON_JOB_MAIN_JS = `/**
86
+ * orchagent scheduled job.
87
+ *
88
+ * Runs on a cron schedule to perform periodic tasks.
89
+ *
90
+ * Schedule examples:
91
+ * orch schedule create org/my-job --cron "0 9 * * 1" # Every Monday 9 AM UTC
92
+ * orch schedule create org/my-job --cron "0 0 * * *" # Daily at midnight
93
+ * orch schedule create org/my-job --cron "0 */6 * * *" # Every 6 hours
94
+ *
95
+ * Local test:
96
+ * echo '{}' | node main.js
97
+ */
98
+
99
+ const fs = require('fs');
100
+
101
+ function main() {
102
+ const raw = fs.readFileSync('/dev/stdin', 'utf-8');
103
+ let data;
104
+ try {
105
+ data = raw.trim() ? JSON.parse(raw) : {};
106
+ } catch {
107
+ console.log(JSON.stringify({ error: 'Invalid JSON input' }));
108
+ process.exit(1);
109
+ }
110
+
111
+ const dryRun = (data.options || {}).dry_run || false;
112
+
113
+ // --- Your scheduled job logic here ---
114
+ // Common patterns:
115
+ // - Fetch data from an API and store results
116
+ // - Generate a daily/weekly report
117
+ // - Clean up old records or files
118
+ // - Send digest notifications (email, Slack, Discord)
119
+ //
120
+ // To use workspace secrets (API keys, webhook URLs):
121
+ // 1. Add to "required_secrets" in orchagent.json
122
+ // 2. Set in workspace: orch secrets set MY_SECRET value
123
+ // 3. Access via: process.env.MY_SECRET
124
+ //
125
+ // Example: send to a webhook
126
+ // const https = require('https');
127
+ // const url = new URL(process.env.WEBHOOK_URL);
128
+ // const req = https.request(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } });
129
+ // req.write(JSON.stringify(payload));
130
+ // req.end();
131
+
132
+ const now = new Date().toISOString();
133
+
134
+ const report = {
135
+ generated_at: now,
136
+ status: dryRun ? 'dry_run' : 'completed',
137
+ summary: 'Scheduled job ran successfully',
138
+ items_processed: 0,
139
+ };
140
+ // --- End your logic ---
141
+
142
+ console.log(JSON.stringify({
143
+ result: report,
144
+ success: true,
145
+ }));
146
+ }
147
+
148
+ main();
149
+ `;
150
+ // ---------------------------------------------------------------------------
151
+ // Schema
152
+ // ---------------------------------------------------------------------------
153
+ exports.CRON_JOB_SCHEMA = `{
154
+ "input": {
155
+ "type": "object",
156
+ "properties": {
157
+ "options": {
158
+ "type": "object",
159
+ "description": "Optional configuration for this run",
160
+ "properties": {
161
+ "dry_run": {
162
+ "type": "boolean",
163
+ "description": "If true, simulate without making changes"
164
+ }
165
+ }
166
+ }
167
+ }
168
+ },
169
+ "output": {
170
+ "type": "object",
171
+ "properties": {
172
+ "result": {
173
+ "type": "object",
174
+ "description": "Job results and summary"
175
+ },
176
+ "success": {
177
+ "type": "boolean",
178
+ "description": "Whether the job completed successfully"
179
+ }
180
+ },
181
+ "required": ["result", "success"]
182
+ }
183
+ }
184
+ `;
185
+ // ---------------------------------------------------------------------------
186
+ // README
187
+ // ---------------------------------------------------------------------------
188
+ function cronJobReadme(agentName) {
189
+ return `# ${agentName}
190
+
191
+ A scheduled job that runs on a cron schedule.
192
+
193
+ ## Setup
194
+
195
+ ### 1. Edit the job logic
196
+
197
+ Edit \`main.py\` (or \`main.js\`) with your scheduled task logic — data processing, report generation, API syncs, notifications, etc.
198
+
199
+ ### 2. Publish
200
+
201
+ \`\`\`sh
202
+ orch publish
203
+ \`\`\`
204
+
205
+ ### 3. Schedule
206
+
207
+ \`\`\`sh
208
+ # Every Monday at 9 AM UTC
209
+ orch schedule create <org>/${agentName} --cron "0 9 * * 1"
210
+
211
+ # Every day at midnight
212
+ orch schedule create <org>/${agentName} --cron "0 0 * * *"
213
+
214
+ # Every 6 hours
215
+ orch schedule create <org>/${agentName} --cron "0 */6 * * *"
216
+ \`\`\`
217
+
218
+ ### 4. Monitor
219
+
220
+ \`\`\`sh
221
+ orch schedule list # View all schedules
222
+ orch logs <org>/${agentName} # View recent runs
223
+ orch metrics <org>/${agentName} # View execution stats
224
+ \`\`\`
225
+
226
+ ## Common Cron Patterns
227
+
228
+ | Pattern | Schedule |
229
+ |---------|----------|
230
+ | \`0 9 * * 1\` | Every Monday at 9 AM |
231
+ | \`0 0 * * *\` | Daily at midnight |
232
+ | \`0 */6 * * *\` | Every 6 hours |
233
+ | \`0 9 * * 1-5\` | Weekdays at 9 AM |
234
+ | \`0 0 1 * *\` | First of each month |
235
+
236
+ ## Input
237
+
238
+ The job receives optional input when triggered:
239
+
240
+ | Field | Type | Description |
241
+ |-------|------|-------------|
242
+ | \`options.dry_run\` | boolean | If true, simulate without making changes |
243
+
244
+ You can also trigger manually with custom input:
245
+
246
+ \`\`\`sh
247
+ orch run <org>/${agentName} --data '{"options": {"dry_run": true}}'
248
+ \`\`\`
249
+
250
+ ## Environment Variables
251
+
252
+ To use API keys or webhook URLs, add them to \`required_secrets\` in orchagent.json, then set them in your workspace:
253
+
254
+ \`\`\`sh
255
+ orch secrets set MY_API_KEY <value>
256
+ orch secrets set WEBHOOK_URL <value>
257
+ \`\`\`
258
+ `;
259
+ }
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  const config_1 = require("../lib/config");
11
11
  const api_1 = require("../lib/api");
12
+ const errors_1 = require("../lib/errors");
12
13
  const analytics_1 = require("../lib/analytics");
13
14
  const adapters_1 = require("../adapters");
14
15
  const installed_1 = require("../lib/installed");
@@ -16,20 +17,47 @@ const agents_md_utils_1 = require("../lib/agents-md-utils");
16
17
  async function fetchLatestAgent(config, agentRef) {
17
18
  const [org, name] = agentRef.split('/');
18
19
  if (!org || !name)
19
- return null;
20
+ return { status: 'bad_ref' };
21
+ // Try public endpoint first
20
22
  try {
21
- // Try to get latest version
22
23
  const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/latest`);
23
24
  return {
25
+ status: 'found',
24
26
  agent: { ...agent, org_slug: org },
25
- latestVersion: agent.version
27
+ latestVersion: agent.version,
28
+ private: false,
26
29
  };
27
30
  }
28
31
  catch (err) {
29
- if (err instanceof api_1.ApiError && err.status === 404) {
30
- return null;
32
+ // Network errors should propagate don't confuse with 404
33
+ if (err instanceof errors_1.NetworkError)
34
+ throw err;
35
+ if (!(err instanceof api_1.ApiError) || err.status !== 404)
36
+ throw err;
37
+ }
38
+ // Public endpoint returned 404 — try authenticated endpoint for private agents
39
+ if (!config.apiKey) {
40
+ return { status: 'not_found_no_auth' };
41
+ }
42
+ try {
43
+ const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
44
+ const userOrg = await (0, api_1.getOrg)(config, workspaceId);
45
+ if (userOrg.slug !== org) {
46
+ return { status: 'not_found' };
31
47
  }
32
- throw err;
48
+ const myAgent = await (0, api_1.getMyAgent)(config, name, 'latest', workspaceId);
49
+ if (!myAgent) {
50
+ return { status: 'not_found' };
51
+ }
52
+ return {
53
+ status: 'found',
54
+ agent: { ...myAgent, org_slug: org },
55
+ latestVersion: myAgent.version,
56
+ private: true,
57
+ };
58
+ }
59
+ catch {
60
+ return { status: 'not_found' };
33
61
  }
34
62
  }
35
63
  function registerUpdateCommand(program) {
@@ -77,11 +105,20 @@ function registerUpdateCommand(program) {
77
105
  fileStatuses.push({ item, status: await (0, installed_1.checkModified)(item) });
78
106
  }
79
107
  // Fetch latest version once per agent
80
- const latest = await fetchLatestAgent(resolved, agentName);
81
- if (!latest) {
82
- process.stdout.write(` ${chalk_1.default.yellow('?')} ${agentName} - could not fetch latest\n`);
108
+ const result = await fetchLatestAgent(resolved, agentName);
109
+ if (result.status === 'not_found_no_auth') {
110
+ process.stdout.write(` ${chalk_1.default.yellow('?')} ${agentName} - not found publicly. Log in with ${chalk_1.default.cyan('orch login')} to check private agents.\n`);
111
+ continue;
112
+ }
113
+ if (result.status === 'not_found') {
114
+ process.stdout.write(` ${chalk_1.default.yellow('?')} ${agentName} - agent not found\n`);
115
+ continue;
116
+ }
117
+ if (result.status === 'bad_ref') {
118
+ process.stdout.write(` ${chalk_1.default.yellow('?')} ${agentName} - invalid agent reference\n`);
83
119
  continue;
84
120
  }
121
+ const latest = result;
85
122
  // Use the version from the first entry (all entries for the same
86
123
  // agent should share the same version after install/update)
87
124
  const installedVersion = entries[0].version;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ /**
3
+ * `orch validate` — Validate agent or skill configuration without publishing.
4
+ *
5
+ * Runs all pre-publish checks locally: config files, schemas, prompt,
6
+ * dependencies, code scanning. Optionally runs server-side validation
7
+ * with --server flag.
8
+ *
9
+ * Exit codes: 0 = valid, 1 = errors found
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.registerValidateCommand = registerValidateCommand;
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ const config_1 = require("../lib/config");
18
+ const api_1 = require("../lib/api");
19
+ const validate_1 = require("../lib/validate");
20
+ const publish_1 = require("./publish");
21
+ const errors_1 = require("../lib/errors");
22
+ const analytics_1 = require("../lib/analytics");
23
+ function issueIcon(level) {
24
+ switch (level) {
25
+ case 'error': return chalk_1.default.red('✗');
26
+ case 'warning': return chalk_1.default.yellow('⚠');
27
+ case 'info': return chalk_1.default.blue('ℹ');
28
+ }
29
+ }
30
+ function formatIssue(issue) {
31
+ const icon = issueIcon(issue.level);
32
+ const file = issue.file ? chalk_1.default.dim(` (${issue.file})`) : '';
33
+ return ` ${icon} ${issue.message}${file}`;
34
+ }
35
+ function printResult(result, serverIssues) {
36
+ const allIssues = [...result.issues, ...(serverIssues || [])];
37
+ const errors = allIssues.filter(i => i.level === 'error');
38
+ const warnings = allIssues.filter(i => i.level === 'warning');
39
+ const infos = allIssues.filter(i => i.level === 'info');
40
+ const m = result.metadata;
41
+ // Header
42
+ const label = m.isSkill ? 'skill' : 'agent';
43
+ const name = m.agentName || '(unknown)';
44
+ process.stderr.write(`\nValidating ${label}: ${chalk_1.default.bold(name)}\n\n`);
45
+ // Summary line: type, engine, mode
46
+ if (!m.isSkill && m.agentType && m.executionEngine) {
47
+ process.stderr.write(chalk_1.default.dim(` Type: ${m.agentType} (${m.executionEngine}), Run mode: ${m.runMode || 'on_demand'}\n\n`));
48
+ }
49
+ // All issues sorted: errors first, then warnings, then info
50
+ for (const issue of errors)
51
+ process.stderr.write(formatIssue(issue) + '\n');
52
+ for (const issue of warnings)
53
+ process.stderr.write(formatIssue(issue) + '\n');
54
+ for (const issue of infos)
55
+ process.stderr.write(formatIssue(issue) + '\n');
56
+ // Metadata summary (only if no critical errors)
57
+ if (errors.length === 0 && !m.isSkill) {
58
+ if (m.hasPrompt)
59
+ process.stderr.write(` ${chalk_1.default.green('✓')} prompt.md found\n`);
60
+ if (m.hasSchema)
61
+ process.stderr.write(` ${chalk_1.default.green('✓')} schema.json found\n`);
62
+ if (m.executionEngine === 'managed_loop') {
63
+ process.stderr.write(` ${chalk_1.default.green('✓')} Custom tools: ${m.customToolCount}, Max turns: ${m.maxTurns || 25}\n`);
64
+ }
65
+ if (m.bundleEntrypoint) {
66
+ process.stderr.write(` ${chalk_1.default.green('✓')} Entrypoint: ${m.bundleEntrypoint}\n`);
67
+ }
68
+ if (m.bundleSizeBytes !== undefined && m.bundleFileCount !== undefined) {
69
+ process.stderr.write(` ${chalk_1.default.green('✓')} Bundle: ${m.bundleFileCount} files, ${(m.bundleSizeBytes / 1024).toFixed(1)} KB\n`);
70
+ }
71
+ if (m.sdkCompatible) {
72
+ process.stderr.write(` ${chalk_1.default.green('✓')} SDK detected (Local Ready)\n`);
73
+ }
74
+ }
75
+ // Final verdict
76
+ process.stderr.write('\n');
77
+ const parts = [];
78
+ if (warnings.length > 0)
79
+ parts.push(`${warnings.length} warning${warnings.length > 1 ? 's' : ''}`);
80
+ if (infos.length > 0)
81
+ parts.push(`${infos.length} note${infos.length > 1 ? 's' : ''}`);
82
+ const suffix = parts.length > 0 ? ` (${parts.join(', ')})` : '';
83
+ if (errors.length > 0) {
84
+ process.stderr.write(chalk_1.default.red.bold(`✗ Validation failed: ${errors.length} error${errors.length > 1 ? 's' : ''}${suffix}\n`));
85
+ }
86
+ else {
87
+ process.stderr.write(chalk_1.default.green.bold(`✓ Validation passed${suffix}\n`));
88
+ }
89
+ }
90
+ function toJsonOutput(result, serverIssues) {
91
+ const allIssues = [...result.issues, ...(serverIssues || [])];
92
+ return {
93
+ valid: result.valid && allIssues.filter(i => i.level === 'error').length === 0,
94
+ errors: allIssues.filter(i => i.level === 'error').map(i => ({ message: i.message, file: i.file })),
95
+ warnings: allIssues.filter(i => i.level === 'warning').map(i => ({ message: i.message, file: i.file })),
96
+ info: allIssues.filter(i => i.level === 'info').map(i => ({ message: i.message, file: i.file })),
97
+ metadata: {
98
+ name: result.metadata.agentName,
99
+ type: result.metadata.agentType,
100
+ execution_engine: result.metadata.executionEngine,
101
+ run_mode: result.metadata.runMode,
102
+ is_skill: result.metadata.isSkill,
103
+ has_prompt: result.metadata.hasPrompt,
104
+ has_schema: result.metadata.hasSchema,
105
+ sdk_compatible: result.metadata.sdkCompatible,
106
+ entrypoint: result.metadata.bundleEntrypoint,
107
+ custom_tools: result.metadata.customToolCount,
108
+ max_turns: result.metadata.maxTurns,
109
+ bundle_size_bytes: result.metadata.bundleSizeBytes,
110
+ bundle_file_count: result.metadata.bundleFileCount,
111
+ },
112
+ };
113
+ }
114
+ function registerValidateCommand(program) {
115
+ program
116
+ .command('validate')
117
+ .alias('lint')
118
+ .description('Validate agent or skill configuration without publishing')
119
+ .option('--profile <name>', 'Use API key from named profile')
120
+ .option('--json', 'Output as JSON (for CI/CD)')
121
+ .option('--server', 'Also run server-side validation (requires auth)')
122
+ .option('--url <url>', 'Agent URL (for code-based agents without local code)')
123
+ .option('--docker', 'Validate with Dockerfile inclusion')
124
+ .action(async (options) => {
125
+ const cwd = process.cwd();
126
+ // Run local validation
127
+ const result = await (0, validate_1.validateAgentProject)(cwd, {
128
+ url: options.url,
129
+ docker: options.docker,
130
+ });
131
+ // Online checks (dependencies + server-side validation)
132
+ const serverIssues = [];
133
+ if (options.server || result.metadata.manifest?.manifest?.dependencies?.length) {
134
+ try {
135
+ const config = await (0, config_1.getResolvedConfig)({}, options.profile);
136
+ if (!config.apiKey) {
137
+ if (options.server) {
138
+ serverIssues.push({
139
+ level: 'warning',
140
+ message: 'Server validation skipped: not logged in. Run `orch login` first.',
141
+ });
142
+ }
143
+ }
144
+ else {
145
+ // Resolve workspace
146
+ const configFile = await (0, config_1.loadConfig)();
147
+ let workspaceId;
148
+ if (configFile.workspace && !options.profile) {
149
+ try {
150
+ const { workspaces } = await (0, api_1.request)(config, 'GET', '/workspaces');
151
+ const ws = workspaces.find(w => w.slug === configFile.workspace);
152
+ if (ws)
153
+ workspaceId = ws.id;
154
+ }
155
+ catch { /* skip workspace resolution */ }
156
+ }
157
+ // Dependency checks
158
+ const deps = result.metadata.manifest?.manifest?.dependencies;
159
+ if (deps?.length) {
160
+ try {
161
+ const org = await (0, api_1.getOrg)(config, workspaceId);
162
+ const depResults = await (0, publish_1.checkDependencies)(config, deps, org.slug, workspaceId);
163
+ const notFound = depResults.filter(r => r.status === 'not_found');
164
+ const notCallable = depResults.filter(r => r.status === 'found_not_callable');
165
+ if (notFound.length > 0) {
166
+ for (const dep of notFound) {
167
+ serverIssues.push({
168
+ level: 'warning',
169
+ message: `Unpublished dependency: ${dep.ref}`,
170
+ });
171
+ }
172
+ }
173
+ if (notCallable.length > 0) {
174
+ for (const dep of notCallable) {
175
+ serverIssues.push({
176
+ level: 'warning',
177
+ message: `Dependency has callable: false: ${dep.ref}`,
178
+ });
179
+ }
180
+ }
181
+ }
182
+ catch {
183
+ serverIssues.push({
184
+ level: 'warning',
185
+ message: 'Could not check dependencies (network error)',
186
+ });
187
+ }
188
+ }
189
+ // Server-side validation
190
+ if (options.server && result.valid && result.metadata.agentName) {
191
+ try {
192
+ const m = result.metadata;
193
+ const manifest = m.manifest;
194
+ const validation = await (0, api_1.validateAgentPublish)(config, {
195
+ name: m.agentName,
196
+ type: m.agentType || 'agent',
197
+ run_mode: m.runMode,
198
+ callable: m.callable,
199
+ description: manifest?.description,
200
+ is_public: false,
201
+ supported_providers: m.supportedProviders,
202
+ default_models: manifest?.default_models,
203
+ timeout_seconds: manifest?.timeout_seconds,
204
+ manifest: manifest?.manifest,
205
+ required_secrets: m.requiredSecrets,
206
+ default_skills: manifest?.default_skills,
207
+ skills_locked: manifest?.skills_locked,
208
+ environment: manifest?.environment,
209
+ }, workspaceId);
210
+ if (validation.warnings?.length) {
211
+ for (const w of validation.warnings) {
212
+ serverIssues.push({ level: 'warning', message: `Server: ${w}` });
213
+ }
214
+ }
215
+ if (!validation.valid) {
216
+ for (const e of validation.errors) {
217
+ serverIssues.push({ level: 'error', message: `Server: ${e}` });
218
+ }
219
+ }
220
+ else {
221
+ serverIssues.push({ level: 'info', message: 'Server-side validation passed' });
222
+ }
223
+ }
224
+ catch {
225
+ serverIssues.push({
226
+ level: 'warning',
227
+ message: 'Could not reach server for validation (offline?)',
228
+ });
229
+ }
230
+ }
231
+ }
232
+ }
233
+ catch {
234
+ if (options.server) {
235
+ serverIssues.push({
236
+ level: 'warning',
237
+ message: 'Server validation skipped: authentication error',
238
+ });
239
+ }
240
+ }
241
+ }
242
+ const allValid = result.valid && serverIssues.filter(i => i.level === 'error').length === 0;
243
+ await (0, analytics_1.track)('cli_validate', {
244
+ valid: allValid,
245
+ errors: [...result.issues, ...serverIssues].filter(i => i.level === 'error').length,
246
+ warnings: [...result.issues, ...serverIssues].filter(i => i.level === 'warning').length,
247
+ type: result.metadata.agentType,
248
+ engine: result.metadata.executionEngine,
249
+ server: options.server || false,
250
+ json: options.json || false,
251
+ });
252
+ if (options.json) {
253
+ process.stdout.write(JSON.stringify(toJsonOutput(result, serverIssues), null, 2) + '\n');
254
+ }
255
+ else {
256
+ printResult(result, serverIssues);
257
+ }
258
+ if (!allValid) {
259
+ const err = new errors_1.CliError('Validation failed', errors_1.ExitCodes.INVALID_INPUT);
260
+ err.displayed = true;
261
+ throw err;
262
+ }
263
+ });
264
+ }