@orchagent/cli 0.3.39 → 0.3.40

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.
@@ -84,10 +84,10 @@ if __name__ == "__main__":
84
84
  function readmeTemplate(agentName, type) {
85
85
  const callExample = type === 'code'
86
86
  ? `orchagent call ${agentName} input-file.txt`
87
- : `orchagent call ${agentName} --data '{"input": "Hello world"}'`;
87
+ : `orchagent call ${agentName} --data '{"${type === 'agentic' ? 'task' : 'input'}": "Hello world"}'`;
88
88
  const runExample = type === 'code'
89
89
  ? `orchagent run ${agentName} --input '{"file_path": "src/app.py"}'`
90
- : `orchagent run ${agentName} --input '{"input": "Hello world"}'`;
90
+ : `orchagent run ${agentName} --input '{"${type === 'agentic' ? 'task' : 'input'}": "Hello world"}'`;
91
91
  return `# ${agentName}
92
92
 
93
93
  A brief description of what this agent does.
@@ -119,6 +119,61 @@ ${runExample}
119
119
  | \`result\` | string | The agent's response |
120
120
  `;
121
121
  }
122
+ const AGENTIC_MANIFEST_TEMPLATE = `{
123
+ "name": "my-agent",
124
+ "description": "An agentic AI agent with tool use",
125
+ "type": "agentic",
126
+ "supported_providers": ["anthropic"],
127
+ "max_turns": 25,
128
+ "timeout_seconds": 300,
129
+ "custom_tools": [
130
+ {
131
+ "name": "run_tests",
132
+ "description": "Run the test suite",
133
+ "command": "pytest"
134
+ }
135
+ ]
136
+ }
137
+ `;
138
+ const AGENTIC_PROMPT_TEMPLATE = `You are a helpful AI agent with access to a sandboxed environment.
139
+
140
+ Given the input, complete the task using the available tools:
141
+ - Use bash to run commands
142
+ - Use read_file and write_file to work with files
143
+ - Use custom tools defined by the agent author
144
+ - Call submit_result when you're done
145
+
146
+ Input: The caller's input will be provided as JSON.
147
+
148
+ Work step by step, verify your results, and submit the final output.
149
+ `;
150
+ const AGENTIC_SCHEMA_TEMPLATE = `{
151
+ "input": {
152
+ "type": "object",
153
+ "properties": {
154
+ "task": {
155
+ "type": "string",
156
+ "description": "The task to complete"
157
+ }
158
+ },
159
+ "required": ["task"]
160
+ },
161
+ "output": {
162
+ "type": "object",
163
+ "properties": {
164
+ "result": {
165
+ "type": "string",
166
+ "description": "The result of the task"
167
+ },
168
+ "success": {
169
+ "type": "boolean",
170
+ "description": "Whether the task completed successfully"
171
+ }
172
+ },
173
+ "required": ["result", "success"]
174
+ }
175
+ }
176
+ `;
122
177
  const SKILL_TEMPLATE = `---
123
178
  name: my-skill
124
179
  description: When to use this skill
@@ -134,7 +189,7 @@ function registerInitCommand(program) {
134
189
  .command('init')
135
190
  .description('Initialize a new agent project')
136
191
  .argument('[name]', 'Agent name (default: current directory name)')
137
- .option('--type <type>', 'Type: prompt, code, or skill (default: prompt)', 'prompt')
192
+ .option('--type <type>', 'Type: prompt, code, agentic, or skill (default: prompt)', 'prompt')
138
193
  .action(async (name, options) => {
139
194
  const cwd = process.cwd();
140
195
  // When a name is provided, create a subdirectory for the project
@@ -187,21 +242,30 @@ function registerInitCommand(program) {
187
242
  throw err;
188
243
  }
189
244
  }
190
- // Create manifest
191
- const manifest = JSON.parse(MANIFEST_TEMPLATE);
192
- manifest.name = agentName;
193
- manifest.type = ['code', 'skill'].includes(options.type) ? options.type : 'prompt';
194
- await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
195
- // Create prompt template (for prompt-based agents) or entrypoint (for code agents)
196
- if (options.type === 'code') {
197
- const entrypointPath = path_1.default.join(targetDir, 'main.py');
198
- await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
245
+ // Create manifest and type-specific files
246
+ if (options.type === 'agentic') {
247
+ const manifest = JSON.parse(AGENTIC_MANIFEST_TEMPLATE);
248
+ manifest.name = agentName;
249
+ await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
250
+ await promises_1.default.writeFile(promptPath, AGENTIC_PROMPT_TEMPLATE);
251
+ await promises_1.default.writeFile(schemaPath, AGENTIC_SCHEMA_TEMPLATE);
199
252
  }
200
253
  else {
201
- await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
254
+ const manifest = JSON.parse(MANIFEST_TEMPLATE);
255
+ manifest.name = agentName;
256
+ manifest.type = ['code', 'skill'].includes(options.type) ? options.type : 'prompt';
257
+ await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
258
+ // Create prompt template (for prompt-based agents) or entrypoint (for code agents)
259
+ if (options.type === 'code') {
260
+ const entrypointPath = path_1.default.join(targetDir, 'main.py');
261
+ await promises_1.default.writeFile(entrypointPath, CODE_TEMPLATE_PY);
262
+ }
263
+ else {
264
+ await promises_1.default.writeFile(promptPath, PROMPT_TEMPLATE);
265
+ }
266
+ // Create schema template
267
+ await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
202
268
  }
203
- // Create schema template
204
- await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
205
269
  // Create README
206
270
  const readmePath = path_1.default.join(targetDir, 'README.md');
207
271
  await promises_1.default.writeFile(readmePath, readmeTemplate(agentName, options.type));
@@ -218,7 +282,17 @@ function registerInitCommand(program) {
218
282
  process.stdout.write(` ${prefix}schema.json - Input/output schemas\n`);
219
283
  process.stdout.write(` ${prefix}README.md - Agent documentation\n`);
220
284
  process.stdout.write(`\nNext steps:\n`);
221
- if (options.type !== 'code') {
285
+ if (options.type === 'agentic') {
286
+ const stepNum = name ? 2 : 1;
287
+ if (name) {
288
+ process.stdout.write(` 1. cd ${name}\n`);
289
+ }
290
+ process.stdout.write(` ${stepNum}. Edit prompt.md with your agent instructions\n`);
291
+ process.stdout.write(` ${stepNum + 1}. Edit custom_tools in orchagent.json for your environment\n`);
292
+ process.stdout.write(` ${stepNum + 2}. Edit schema.json with your input/output schemas\n`);
293
+ process.stdout.write(` ${stepNum + 3}. Run: orchagent publish\n`);
294
+ }
295
+ else if (options.type !== 'code') {
222
296
  const stepNum = name ? 2 : 1;
223
297
  if (name) {
224
298
  process.stdout.write(` 1. cd ${name}\n`);
@@ -328,16 +328,16 @@ function registerPublishCommand(program) {
328
328
  ` }\n\n` +
329
329
  `See docs/manifest.md for details.`);
330
330
  }
331
- // Read prompt (for prompt-based agents and skills)
331
+ // Read prompt (for prompt-based, agentic, and skill agents)
332
332
  let prompt;
333
- if (manifest.type === 'prompt' || manifest.type === 'skill') {
333
+ if (manifest.type === 'prompt' || manifest.type === 'skill' || manifest.type === 'agentic') {
334
334
  const promptPath = path_1.default.join(cwd, 'prompt.md');
335
335
  try {
336
336
  prompt = await promises_1.default.readFile(promptPath, 'utf-8');
337
337
  }
338
338
  catch (err) {
339
339
  if (err.code === 'ENOENT') {
340
- const agentTypeName = manifest.type === 'skill' ? 'skill' : 'prompt-based agent';
340
+ const agentTypeName = manifest.type === 'skill' ? 'skill' : manifest.type === 'agentic' ? 'agentic agent' : 'prompt-based agent';
341
341
  throw new errors_1.CliError(`No prompt.md found for ${agentTypeName}.\n\n` +
342
342
  'Create a prompt.md file in the current directory with your prompt template.\n' +
343
343
  'See: https://orchagent.io/docs/publishing');
@@ -345,6 +345,39 @@ function registerPublishCommand(program) {
345
345
  throw err;
346
346
  }
347
347
  }
348
+ // For agentic agents, validate custom_tools and build manifest
349
+ if (manifest.type === 'agentic') {
350
+ // Validate custom_tools format
351
+ if (manifest.custom_tools) {
352
+ for (const tool of manifest.custom_tools) {
353
+ if (!tool.name || !tool.command) {
354
+ throw new errors_1.CliError(`Invalid custom_tool: each tool must have 'name' and 'command' fields.\n` +
355
+ `Found: ${JSON.stringify(tool)}`);
356
+ }
357
+ }
358
+ }
359
+ // Validate max_turns
360
+ if (manifest.max_turns !== undefined) {
361
+ if (typeof manifest.max_turns !== 'number' || manifest.max_turns < 1 || manifest.max_turns > 50) {
362
+ throw new errors_1.CliError('max_turns must be a number between 1 and 50');
363
+ }
364
+ }
365
+ // Store agentic config in manifest field
366
+ const agenticManifest = {
367
+ ...(manifest.manifest || {}),
368
+ };
369
+ if (manifest.custom_tools) {
370
+ agenticManifest.custom_tools = manifest.custom_tools;
371
+ }
372
+ if (manifest.max_turns) {
373
+ agenticManifest.max_turns = manifest.max_turns;
374
+ }
375
+ manifest.manifest = agenticManifest;
376
+ // Agentic agents default to anthropic provider
377
+ if (!manifest.supported_providers) {
378
+ manifest.supported_providers = ['anthropic'];
379
+ }
380
+ }
348
381
  // Read schemas
349
382
  let inputSchema;
350
383
  let outputSchema;
@@ -363,7 +396,8 @@ function registerPublishCommand(program) {
363
396
  throw new errors_1.CliError(`Failed to read schema.json: ${err}`);
364
397
  }
365
398
  }
366
- // For prompt agents, derive input schema from template variables if needed
399
+ // For prompt/skill agents, derive input schema from template variables if needed
400
+ // (Agentic agents use schema.json directly — no template variable derivation)
367
401
  if (prompt && (manifest.type === 'prompt' || manifest.type === 'skill')) {
368
402
  const templateVars = extractTemplateVariables(prompt);
369
403
  if (templateVars.length > 0) {
@@ -391,9 +425,26 @@ function registerPublishCommand(program) {
391
425
  }
392
426
  }
393
427
  // For code-based agents, either --url is required OR we bundle the code
428
+ // For agentic agents, use internal placeholder (no user code, platform handles execution)
394
429
  let agentUrl = options.url;
395
430
  let shouldUploadBundle = false;
396
- if (manifest.type === 'code' && !options.url) {
431
+ if (manifest.type === 'agentic') {
432
+ // Agentic agents don't need a URL or code bundle
433
+ agentUrl = agentUrl || 'https://agentic-agent.internal';
434
+ // But they can include a Dockerfile for custom environments
435
+ if (options.docker) {
436
+ const dockerfilePath = path_1.default.join(cwd, 'Dockerfile');
437
+ try {
438
+ await promises_1.default.access(dockerfilePath);
439
+ shouldUploadBundle = true;
440
+ process.stdout.write(`Including Dockerfile for custom environment\n`);
441
+ }
442
+ catch {
443
+ throw new errors_1.CliError('--docker flag specified but no Dockerfile found in project directory');
444
+ }
445
+ }
446
+ }
447
+ else if (manifest.type === 'code' && !options.url) {
397
448
  // Check if this looks like a Python or JS project that can be bundled
398
449
  const entrypoint = manifest.entrypoint || await (0, bundle_1.detectEntrypoint)(cwd);
399
450
  if (entrypoint) {
@@ -441,6 +492,18 @@ function registerPublishCommand(program) {
441
492
  process.stderr.write(` ✓ Input schema derived from template variables: ${vars.join(', ')}\n`);
442
493
  }
443
494
  }
495
+ else if (manifest.type === 'agentic') {
496
+ // Agentic agent validations
497
+ const promptBytes = prompt ? Buffer.byteLength(prompt, 'utf-8') : 0;
498
+ process.stderr.write(` ✓ prompt.md found (${promptBytes.toLocaleString()} bytes)\n`);
499
+ if (schemaFromFile) {
500
+ const schemaTypes = [inputSchema ? 'input' : null, outputSchema ? 'output' : null].filter(Boolean).join(' + ');
501
+ process.stderr.write(` ✓ schema.json found (${schemaTypes} schemas)\n`);
502
+ }
503
+ const customToolCount = manifest.custom_tools?.length || 0;
504
+ process.stderr.write(` ✓ Custom tools: ${customToolCount}\n`);
505
+ process.stderr.write(` ✓ Max turns: ${manifest.max_turns || 25}\n`);
506
+ }
444
507
  else if (manifest.type === 'code') {
445
508
  // Code agent validations
446
509
  const entrypoint = manifest.entrypoint || await (0, bundle_1.detectEntrypoint)(cwd);
@@ -522,7 +585,7 @@ function registerPublishCommand(program) {
522
585
  }
523
586
  const assignedVersion = result.agent?.version || 'v1';
524
587
  const agentId = result.agent?.id;
525
- // Upload code bundle if this is a hosted code agent
588
+ // Upload code bundle if this is a hosted code agent or agentic agent with --docker
526
589
  if (shouldUploadBundle && agentId) {
527
590
  process.stdout.write(`\nBundling code...\n`);
528
591
  const tempDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'orchagent-bundle-'));
@@ -541,8 +604,20 @@ function registerPublishCommand(program) {
541
604
  throw new errors_1.CliError('--docker flag specified but no Dockerfile found in project directory');
542
605
  }
543
606
  }
607
+ // For agentic agents, also include requirements.txt if present
608
+ if (manifest.type === 'agentic') {
609
+ const reqPath = path_1.default.join(cwd, 'requirements.txt');
610
+ try {
611
+ await promises_1.default.access(reqPath);
612
+ includePatterns.push('requirements.txt');
613
+ process.stdout.write(` Including requirements.txt for sandbox dependencies\n`);
614
+ }
615
+ catch {
616
+ // Optional
617
+ }
618
+ }
544
619
  const bundleResult = await (0, bundle_1.createCodeBundle)(cwd, bundlePath, {
545
- entrypoint: manifest.entrypoint,
620
+ entrypoint: manifest.type === 'agentic' ? undefined : manifest.entrypoint,
546
621
  exclude: manifest.bundle?.exclude,
547
622
  include: includePatterns.length > 0 ? includePatterns : undefined,
548
623
  });
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.extractCount = extractCount;
7
7
  exports.registerSecurityCommand = registerSecurityCommand;
8
+ exports.generateMarkdownReport = generateMarkdownReport;
8
9
  const promises_1 = __importDefault(require("fs/promises"));
9
10
  const chalk_1 = __importDefault(require("chalk"));
10
11
  const config_1 = require("../lib/config");
@@ -82,26 +83,28 @@ function formatSummaryOutput(result) {
82
83
  // Summary stats
83
84
  process.stdout.write(`${chalk_1.default.bold('Attacks Tested:')} ${result.total_attacks}\n`);
84
85
  process.stdout.write(`${chalk_1.default.bold('Vulnerabilities Found:')} ${result.vulnerabilities_found}\n\n`);
85
- // Breakdown by severity
86
+ // Breakdown by severity — show all tested levels, not just those with leaks
86
87
  if (Object.keys(result.summary.by_severity).length > 0) {
87
88
  process.stdout.write(chalk_1.default.bold('By Severity:\n'));
88
89
  const severityOrder = ['critical', 'high', 'medium', 'low'];
89
- for (const sev of severityOrder) {
90
- const count = extractCount(result.summary.by_severity[sev]);
91
- if (count > 0) {
92
- process.stdout.write(` ${severityColor(sev)}: ${count}\n`);
93
- }
90
+ const entries = Object.entries(result.summary.by_severity)
91
+ .sort(([a], [b]) => {
92
+ const ai = severityOrder.indexOf(a);
93
+ const bi = severityOrder.indexOf(b);
94
+ return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
95
+ });
96
+ for (const [sev, rawCount] of entries) {
97
+ const count = extractCount(rawCount);
98
+ process.stdout.write(` ${severityColor(sev)}: ${count}\n`);
94
99
  }
95
100
  process.stdout.write('\n');
96
101
  }
97
- // Breakdown by category
102
+ // Breakdown by category — show all tested categories
98
103
  if (Object.keys(result.summary.by_category).length > 0) {
99
104
  process.stdout.write(chalk_1.default.bold('By Category:\n'));
100
105
  for (const [cat, rawCount] of Object.entries(result.summary.by_category)) {
101
106
  const count = extractCount(rawCount);
102
- if (count > 0) {
103
- process.stdout.write(` ${cat}: ${count}\n`);
104
- }
107
+ process.stdout.write(` ${cat}: ${count}\n`);
105
108
  }
106
109
  process.stdout.write('\n');
107
110
  }
@@ -305,9 +308,7 @@ function generateMarkdownReport(result) {
305
308
  lines.push('');
306
309
  for (const [sev, rawCount] of Object.entries(result.summary.by_severity)) {
307
310
  const count = extractCount(rawCount);
308
- if (count > 0) {
309
- lines.push(`- ${sev.toUpperCase()}: ${count}`);
310
- }
311
+ lines.push(`- ${sev.toUpperCase()}: ${count}`);
311
312
  }
312
313
  lines.push('');
313
314
  }
@@ -316,9 +317,7 @@ function generateMarkdownReport(result) {
316
317
  lines.push('');
317
318
  for (const [cat, rawCount] of Object.entries(result.summary.by_category)) {
318
319
  const count = extractCount(rawCount);
319
- if (count > 0) {
320
- lines.push(`- ${cat}: ${count}`);
321
- }
320
+ lines.push(`- ${cat}: ${count}`);
322
321
  }
323
322
  lines.push('');
324
323
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.39",
3
+ "version": "0.3.40",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",