@orchagent/cli 0.3.45 → 0.3.47

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.
@@ -3,21 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerCallCommand = registerCallCommand;
4
4
  /**
5
5
  * Deprecated: The 'call' command has been merged into 'run'.
6
- * Cloud execution is now the default behavior of 'orch run'.
6
+ * Cloud execution is now the default behavior of 'orchagent run'.
7
7
  * This file provides a thin alias that prints a deprecation notice and exits.
8
8
  */
9
9
  function registerCallCommand(program) {
10
10
  program
11
11
  .command('call')
12
- .description('Deprecated: use "orch run" instead')
12
+ .description('Deprecated: use "orchagent run" instead')
13
13
  .allowUnknownOption()
14
14
  .allowExcessArguments()
15
15
  .action(() => {
16
16
  process.stderr.write(`The 'call' command has been merged into 'run'.\n\n` +
17
- `Cloud execution is now the default behavior of 'orch run':\n` +
18
- ` orch run <agent> --data '{...}' # runs on server (cloud)\n` +
19
- ` orch run <agent> --local --data '...' # runs locally\n\n` +
20
- `Replace 'orch call' with 'orch run' in your commands.\n`);
17
+ `Cloud execution is now the default behavior of 'orchagent run':\n` +
18
+ ` orchagent run <agent> --data '{...}' # runs on server (cloud)\n` +
19
+ ` orchagent run <agent> --local --data '...' # runs locally\n\n` +
20
+ `Replace 'orchagent call' with 'orchagent run' in your commands.\n`);
21
21
  process.exit(1);
22
22
  });
23
23
  }
@@ -40,7 +40,7 @@ Examples:
40
40
  const ref = (0, agent_ref_1.parseAgentRef)(agent);
41
41
  const config = await (0, config_1.getResolvedConfig)();
42
42
  if (!config.apiKey) {
43
- throw new errors_1.CliError('Not logged in. Run `orch login` first.');
43
+ throw new errors_1.CliError('Not logged in. Run `orchagent login` first.');
44
44
  }
45
45
  process.stdout.write('Finding agent...\n');
46
46
  // Find the agent by name, filtering by org if provided
@@ -85,7 +85,7 @@ async function listEnvs(config, options) {
85
85
  const result = await (0, api_1.listEnvironments)(config, workspaceId);
86
86
  if (result.environments.length === 0) {
87
87
  console.log(chalk_1.default.gray('No environments found.'));
88
- console.log(chalk_1.default.gray('Use `orch env create` to create one, or include a Dockerfile in your agent bundle.'));
88
+ console.log(chalk_1.default.gray('Use `orchagent env create` to create one, or include a Dockerfile in your agent bundle.'));
89
89
  return;
90
90
  }
91
91
  const table = new cli_table3_1.default({
@@ -167,7 +167,7 @@ async function createEnv(config, options) {
167
167
  console.log(chalk_1.default.green('Environment created, build started.'));
168
168
  console.log(`Environment ID: ${result.environment.id}`);
169
169
  console.log();
170
- console.log(chalk_1.default.gray(`Check build status: orch env status ${result.environment.id}`));
170
+ console.log(chalk_1.default.gray(`Check build status: orchagent env status ${result.environment.id}`));
171
171
  }
172
172
  await (0, analytics_1.track)('env_create', {
173
173
  environment_id: result.environment.id,
@@ -49,49 +49,27 @@ async function fetchReadme(url) {
49
49
  return null;
50
50
  }
51
51
  }
52
- async function downloadAgentWithFallback(config, org, agent, version) {
53
- // First fetch public metadata to get pricing fields
54
- let publicMeta = null;
52
+ async function getAgentInfo(config, org, agent, version) {
53
+ // Use public metadata endpoint as primary source — never blocked by download restrictions
55
54
  try {
56
- publicMeta = await (0, api_1.getPublicAgent)(config, org, agent, version);
55
+ const publicMeta = await (0, api_1.getPublicAgent)(config, org, agent, version);
56
+ const meta = publicMeta;
57
+ return {
58
+ type: (publicMeta.type || 'tool'),
59
+ name: publicMeta.name,
60
+ version: publicMeta.version,
61
+ description: (publicMeta.description ?? undefined),
62
+ supported_providers: publicMeta.supported_providers || ['any'],
63
+ input_schema: publicMeta.input_schema,
64
+ output_schema: publicMeta.output_schema,
65
+ source_url: meta.source_url,
66
+ run_command: meta.run_command,
67
+ url: meta.url,
68
+ pricing_mode: publicMeta.pricing_mode,
69
+ price_per_call_cents: publicMeta.price_per_call_cents,
70
+ };
57
71
  }
58
72
  catch (err) {
59
- // If public metadata not found, continue to try download
60
- if (!(err instanceof api_1.ApiError) || err.status !== 404) {
61
- // Some other error, rethrow
62
- throw err;
63
- }
64
- }
65
- // Try public download endpoint
66
- try {
67
- const downloadData = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
68
- // Merge pricing fields from publicMeta if not present
69
- if (publicMeta && !downloadData.pricing_mode) {
70
- downloadData.pricing_mode = publicMeta.pricing_mode;
71
- downloadData.price_per_call_cents = publicMeta.price_per_call_cents;
72
- }
73
- return downloadData;
74
- }
75
- catch (err) {
76
- // Handle 403 PAID_AGENT_SERVER_ONLY error
77
- if (err instanceof api_1.ApiError && err.status === 403) {
78
- const payload = err.payload;
79
- if (payload?.error?.code === 'PAID_AGENT_SERVER_ONLY') {
80
- // For non-owners, use public metadata
81
- if (publicMeta) {
82
- return {
83
- type: publicMeta.type,
84
- name: publicMeta.name,
85
- version: publicMeta.version,
86
- description: publicMeta.description,
87
- supported_providers: publicMeta.supported_providers || ['any'],
88
- source_url: publicMeta.source_url,
89
- pricing_mode: publicMeta.pricing_mode,
90
- price_per_call_cents: publicMeta.price_per_call_cents,
91
- };
92
- }
93
- }
94
- }
95
73
  if (!(err instanceof api_1.ApiError) || err.status !== 404)
96
74
  throw err;
97
75
  }
@@ -103,7 +81,6 @@ async function downloadAgentWithFallback(config, org, agent, version) {
103
81
  if (userOrg.slug !== org) {
104
82
  throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
105
83
  }
106
- // Find agent in user's list and construct download data
107
84
  const agents = await (0, api_1.listMyAgents)(config);
108
85
  const matching = agents.filter(a => a.name === agent);
109
86
  if (matching.length === 0) {
@@ -118,10 +95,8 @@ async function downloadAgentWithFallback(config, org, agent, version) {
118
95
  targetAgent = found;
119
96
  }
120
97
  else {
121
- // Get most recent
122
98
  targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
123
99
  }
124
- // Convert Agent to AgentDownload format
125
100
  return {
126
101
  type: targetAgent.type,
127
102
  name: targetAgent.name,
@@ -134,7 +109,6 @@ async function downloadAgentWithFallback(config, org, agent, version) {
134
109
  source_url: targetAgent.source_url,
135
110
  run_command: targetAgent.run_command,
136
111
  url: targetAgent.url,
137
- sdk_compatible: false,
138
112
  pricing_mode: targetAgent.pricing_mode,
139
113
  price_per_call_cents: targetAgent.price_per_call_cents,
140
114
  };
@@ -148,7 +122,7 @@ function registerInfoCommand(program) {
148
122
  const config = await (0, config_1.getResolvedConfig)();
149
123
  const { org, agent, version } = (0, agent_ref_1.parseAgentRef)(agentArg);
150
124
  // Fetch agent metadata
151
- const agentData = await downloadAgentWithFallback(config, org, agent, version);
125
+ const agentData = await getAgentInfo(config, org, agent, version);
152
126
  if (options.json) {
153
127
  // Don't expose internal routing URLs in JSON output
154
128
  const output = { ...agentData };
@@ -161,7 +161,7 @@ function registerInstallCommand(program) {
161
161
  .option('--json', 'Output result as JSON (for automation/tooling)')
162
162
  .addHelpText('after', `
163
163
  Note: Paid agents cannot be installed locally - they run on server only.
164
- Use 'orch run' to execute paid agents.
164
+ Use 'orchagent run' to execute paid agents.
165
165
  `)
166
166
  .action(async (agentArg, options) => {
167
167
  const jsonMode = options.json === true;
@@ -68,7 +68,7 @@ function registerListCommand(program) {
68
68
  }
69
69
  // Warn about orphaned entries if any exist
70
70
  if (orphaned.length > 0) {
71
- process.stdout.write(`\nWarning: ${orphaned.length} tracked installation(s) have missing files. Run 'orch list --verify' to clean up.\n`);
71
+ process.stdout.write(`\nWarning: ${orphaned.length} tracked installation(s) have missing files. Run 'orchagent list --verify' to clean up.\n`);
72
72
  }
73
73
  });
74
74
  }
@@ -657,7 +657,7 @@ function registerPublishCommand(program) {
657
657
  if (uploadResult.environment_source === 'dockerfile_new') {
658
658
  process.stdout.write(` ${chalk_1.default.cyan('Custom environment detected (Dockerfile)')}\n`);
659
659
  process.stdout.write(` ${chalk_1.default.yellow('Environment building...')} Agent will be ready when build completes.\n`);
660
- process.stdout.write(` ${chalk_1.default.gray(`Check status: orch env status ${uploadResult.environment_id}`)}\n`);
660
+ process.stdout.write(` ${chalk_1.default.gray(`Check status: orchagent env status ${uploadResult.environment_id}`)}\n`);
661
661
  }
662
662
  else if (uploadResult.environment_source === 'dockerfile_reused') {
663
663
  process.stdout.write(` ${chalk_1.default.green('Custom environment (reusing existing build)')}\n`);
@@ -74,6 +74,18 @@ function parseAgentRef(value) {
74
74
  }
75
75
  throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
76
76
  }
77
+ // ─── Validation helpers ─────────────────────────────────────────────────────
78
+ async function validateFilePath(filePath) {
79
+ const stat = await promises_1.default.stat(filePath);
80
+ if (stat.isDirectory()) {
81
+ throw new errors_1.CliError(`Cannot upload a directory for cloud execution: ${filePath}\n\n` +
82
+ `Options:\n` +
83
+ ` Use --local to run locally with filesystem access:\n` +
84
+ ` orch run <agent> --local --path ${filePath}\n` +
85
+ ` Or specify individual files:\n` +
86
+ ` orch run <agent> --file ${filePath}/specific-file.ts`);
87
+ }
88
+ }
77
89
  // ─── Cloud execution helpers (from call.ts) ─────────────────────────────────
78
90
  function findLocalPathKey(obj) {
79
91
  if (typeof obj !== 'object' || obj === null) {
@@ -95,7 +107,7 @@ function warnIfLocalPathReference(jsonBody) {
95
107
  process.stderr.write(`Warning: Your payload contains a local path reference ('${pathKey}').\n` +
96
108
  `Remote agents cannot access your local filesystem. The path will be interpreted\n` +
97
109
  `by the server, not your local machine.\n\n` +
98
- `Tip: Use 'orch run <agent> --local' to execute locally with filesystem access.\n\n`);
110
+ `Tip: Use 'orchagent run <agent> --local' to execute locally with filesystem access.\n\n`);
99
111
  }
100
112
  }
101
113
  catch {
@@ -124,6 +136,19 @@ function inferFileField(inputSchema) {
124
136
  return requiredStrings[0];
125
137
  return 'content';
126
138
  }
139
+ function applySchemaDefaults(body, schema) {
140
+ if (!schema)
141
+ return body;
142
+ const props = schema.properties;
143
+ if (!props || typeof props !== 'object')
144
+ return body;
145
+ for (const [key, def] of Object.entries(props)) {
146
+ if (body[key] === undefined && def.default !== undefined) {
147
+ body[key] = def.default;
148
+ }
149
+ }
150
+ return body;
151
+ }
127
152
  async function readStdin() {
128
153
  if (process.stdin.isTTY)
129
154
  return null;
@@ -155,6 +180,7 @@ async function buildMultipartBody(filePaths, metadata) {
155
180
  }
156
181
  const form = new FormData();
157
182
  for (const filePath of filePaths) {
183
+ await validateFilePath(filePath);
158
184
  const buffer = await promises_1.default.readFile(filePath);
159
185
  const filename = path_1.default.basename(filePath);
160
186
  form.append('files[]', new Blob([new Uint8Array(buffer)]), filename);
@@ -182,6 +208,7 @@ async function resolveJsonBody(input) {
182
208
  raw = stdinData.toString('utf8');
183
209
  }
184
210
  else {
211
+ await validateFilePath(source);
185
212
  raw = await promises_1.default.readFile(source, 'utf8');
186
213
  }
187
214
  }
@@ -857,6 +884,224 @@ async function saveBundleLocally(config, org, agent, version, agentId) {
857
884
  }
858
885
  return bundleDir;
859
886
  }
887
+ // ─── Local directory execution ───────────────────────────────────────────────
888
+ function isLocalPath(ref) {
889
+ return ref.startsWith('.') || ref.startsWith('/') || ref.startsWith('~');
890
+ }
891
+ async function executeLocalFromDir(dirPath, args, options) {
892
+ // Merge --data alias into --input
893
+ if (options.data && !options.input) {
894
+ options.input = options.data;
895
+ }
896
+ if (options.here) {
897
+ options.input = JSON.stringify({ path: process.cwd() });
898
+ }
899
+ else if (options.path) {
900
+ options.input = JSON.stringify({ path: options.path });
901
+ }
902
+ const resolved = path_1.default.resolve(dirPath);
903
+ // Verify directory exists
904
+ let stat;
905
+ try {
906
+ stat = await promises_1.default.stat(resolved);
907
+ }
908
+ catch {
909
+ throw new errors_1.CliError(`Directory not found: ${resolved}`);
910
+ }
911
+ if (!stat.isDirectory()) {
912
+ throw new errors_1.CliError(`Not a directory: ${resolved}`);
913
+ }
914
+ // Read orchagent.json manifest
915
+ const manifestPath = path_1.default.join(resolved, 'orchagent.json');
916
+ let manifest;
917
+ try {
918
+ const raw = await promises_1.default.readFile(manifestPath, 'utf-8');
919
+ manifest = JSON.parse(raw);
920
+ }
921
+ catch {
922
+ throw new errors_1.CliError(`No orchagent.json found in ${resolved}\n\n` +
923
+ `To run a local agent, the directory must contain orchagent.json.\n` +
924
+ `Create one with: orch init`);
925
+ }
926
+ const agentType = manifest.type || 'tool';
927
+ if (agentType === 'skill') {
928
+ throw new errors_1.CliError('Skills cannot be run directly.\n\n' +
929
+ 'Skills are instructions meant to be injected into AI agent contexts.\n' +
930
+ `Install with: orchagent skill install <org>/<skill>`);
931
+ }
932
+ if (agentType === 'agent') {
933
+ throw new errors_1.CliError('Agent type cannot be run locally.\n\n' +
934
+ 'Agent type requires a sandbox environment with tool use capabilities.\n' +
935
+ 'Publish first, then run in the cloud:\n' +
936
+ ' orch publish && orch run <org>/<agent> --data \'{"task": "..."}\'');
937
+ }
938
+ if (agentType === 'prompt') {
939
+ // Read prompt.md
940
+ const promptPath = path_1.default.join(resolved, 'prompt.md');
941
+ let prompt;
942
+ try {
943
+ prompt = await promises_1.default.readFile(promptPath, 'utf-8');
944
+ }
945
+ catch {
946
+ throw new errors_1.CliError(`No prompt.md found in ${resolved}`);
947
+ }
948
+ // Read schema.json for schemas
949
+ let inputSchema;
950
+ let outputSchema;
951
+ try {
952
+ const schemaRaw = await promises_1.default.readFile(path_1.default.join(resolved, 'schema.json'), 'utf-8');
953
+ const schemas = JSON.parse(schemaRaw);
954
+ inputSchema = schemas.input;
955
+ outputSchema = schemas.output;
956
+ }
957
+ catch {
958
+ // Schema is optional
959
+ }
960
+ // Build AgentDownload from local files
961
+ const agentData = {
962
+ type: 'prompt',
963
+ name: manifest.name || path_1.default.basename(resolved),
964
+ version: manifest.version || 'local',
965
+ description: manifest.description,
966
+ prompt,
967
+ input_schema: inputSchema,
968
+ output_schema: outputSchema,
969
+ supported_providers: manifest.supported_providers || ['any'],
970
+ default_models: manifest.default_models,
971
+ };
972
+ if (!options.input) {
973
+ process.stderr.write(`Loaded local agent: ${agentData.name}\n\n`);
974
+ process.stderr.write(`Run with input:\n`);
975
+ process.stderr.write(` orch run ${dirPath} --local --data '{...}'\n`);
976
+ return;
977
+ }
978
+ let inputData;
979
+ try {
980
+ inputData = JSON.parse(options.input);
981
+ }
982
+ catch {
983
+ throw new errors_1.CliError('Invalid JSON input');
984
+ }
985
+ const config = await (0, config_1.getResolvedConfig)();
986
+ const result = await executePromptLocally(agentData, inputData, [], config, options.provider, options.model);
987
+ (0, output_1.printJson)(result);
988
+ return;
989
+ }
990
+ // Tool agents with bundle
991
+ const entrypoint = manifest.entrypoint || 'sandbox_main.py';
992
+ const entrypointPath = path_1.default.join(resolved, entrypoint);
993
+ try {
994
+ await promises_1.default.access(entrypointPath);
995
+ }
996
+ catch {
997
+ // No local entrypoint — try run_command
998
+ if (manifest.run_command) {
999
+ const agentData = {
1000
+ type: 'tool',
1001
+ name: manifest.name || path_1.default.basename(resolved),
1002
+ version: manifest.version || 'local',
1003
+ supported_providers: manifest.supported_providers || ['any'],
1004
+ run_command: manifest.run_command,
1005
+ source_url: manifest.source_url,
1006
+ pip_package: manifest.pip_package,
1007
+ };
1008
+ await executeTool(agentData, args);
1009
+ return;
1010
+ }
1011
+ throw new errors_1.CliError(`No entrypoint found in ${resolved}\n\n` +
1012
+ `Expected: ${entrypoint}\n` +
1013
+ `For tool agents, ensure the directory contains the entrypoint file\n` +
1014
+ `or has run_command set in orchagent.json.`);
1015
+ }
1016
+ // Execute bundle-style from local directory
1017
+ const config = await (0, config_1.getResolvedConfig)();
1018
+ const agentData = {
1019
+ type: 'tool',
1020
+ name: manifest.name || path_1.default.basename(resolved),
1021
+ version: manifest.version || 'local',
1022
+ supported_providers: manifest.supported_providers || ['any'],
1023
+ entrypoint,
1024
+ };
1025
+ // Install requirements if present
1026
+ const requirementsPath = path_1.default.join(resolved, 'requirements.txt');
1027
+ try {
1028
+ await promises_1.default.access(requirementsPath);
1029
+ const { code } = await runCommand('python3', ['-m', 'pip', 'install', '-q', '--disable-pip-version-check', '-r', requirementsPath]);
1030
+ if (code !== 0) {
1031
+ process.stderr.write('Warning: Failed to install requirements.txt\n');
1032
+ }
1033
+ }
1034
+ catch {
1035
+ // No requirements.txt
1036
+ }
1037
+ let inputJson = '{}';
1038
+ if (options.input) {
1039
+ try {
1040
+ JSON.parse(options.input);
1041
+ inputJson = options.input;
1042
+ }
1043
+ catch {
1044
+ throw new errors_1.CliError('Invalid JSON input');
1045
+ }
1046
+ }
1047
+ else if (args.length > 0) {
1048
+ inputJson = JSON.stringify({ input: args[0] });
1049
+ }
1050
+ process.stderr.write(`\nRunning: python3 ${entrypoint}\n\n`);
1051
+ const subprocessEnv = { ...process.env };
1052
+ if (config.apiKey) {
1053
+ subprocessEnv.ORCHAGENT_SERVICE_KEY = config.apiKey;
1054
+ subprocessEnv.ORCHAGENT_API_URL = config.apiUrl;
1055
+ }
1056
+ const proc = (0, child_process_1.spawn)('python3', [entrypointPath], {
1057
+ cwd: resolved,
1058
+ stdio: ['pipe', 'pipe', 'pipe'],
1059
+ env: subprocessEnv,
1060
+ });
1061
+ proc.stdin.write(inputJson);
1062
+ proc.stdin.end();
1063
+ let stdout = '';
1064
+ let stderr = '';
1065
+ proc.stdout?.on('data', (data) => {
1066
+ stdout += data.toString();
1067
+ });
1068
+ proc.stderr?.on('data', (data) => {
1069
+ const text = data.toString();
1070
+ stderr += text;
1071
+ process.stderr.write(text);
1072
+ });
1073
+ const exitCode = await new Promise((resolve) => {
1074
+ proc.on('close', (code) => resolve(code ?? 1));
1075
+ proc.on('error', (err) => {
1076
+ process.stderr.write(`Error running agent: ${err.message}\n`);
1077
+ resolve(1);
1078
+ });
1079
+ });
1080
+ if (stdout.trim()) {
1081
+ try {
1082
+ const result = JSON.parse(stdout.trim());
1083
+ if (exitCode !== 0 && typeof result === 'object' && result !== null && 'error' in result) {
1084
+ throw new errors_1.CliError(`Agent error: ${result.error}`);
1085
+ }
1086
+ if (exitCode !== 0) {
1087
+ (0, output_1.printJson)(result);
1088
+ throw new errors_1.CliError(`Agent exited with code ${exitCode}`);
1089
+ }
1090
+ (0, output_1.printJson)(result);
1091
+ }
1092
+ catch (err) {
1093
+ if (err instanceof errors_1.CliError)
1094
+ throw err;
1095
+ process.stdout.write(stdout);
1096
+ if (exitCode !== 0) {
1097
+ throw new errors_1.CliError(`Agent exited with code ${exitCode}`);
1098
+ }
1099
+ }
1100
+ }
1101
+ else if (exitCode !== 0) {
1102
+ throw new errors_1.CliError(`Agent exited with code ${exitCode}`);
1103
+ }
1104
+ }
860
1105
  // ─── Cloud execution path ───────────────────────────────────────────────────
861
1106
  async function executeCloud(agentRef, file, options) {
862
1107
  // Merge --input alias into --data
@@ -1012,10 +1257,58 @@ async function executeCloud(agentRef, file, options) {
1012
1257
  ...(options.file ?? []),
1013
1258
  ...(file ? [file] : []),
1014
1259
  ];
1015
- if (options.data) {
1016
- if (filePaths.length > 0 || options.metadata) {
1017
- throw new errors_1.CliError('Cannot use --data with file uploads or --metadata.');
1260
+ if (options.data && options.metadata) {
1261
+ throw new errors_1.CliError('Cannot use --data with --metadata. Use one or the other.');
1262
+ }
1263
+ if (options.data && filePaths.length > 0) {
1264
+ // Merge file content into --data
1265
+ const resolvedBody = await resolveJsonBody(options.data);
1266
+ const bodyObj = JSON.parse(resolvedBody);
1267
+ if (agentMeta.type === 'prompt') {
1268
+ const fieldName = options.fileField || inferFileField(agentMeta.input_schema);
1269
+ if (filePaths.length === 1) {
1270
+ await validateFilePath(filePaths[0]);
1271
+ bodyObj[fieldName] = await promises_1.default.readFile(filePaths[0], 'utf-8');
1272
+ sourceLabel = filePaths[0];
1273
+ }
1274
+ else {
1275
+ const allContents = {};
1276
+ for (const fp of filePaths) {
1277
+ await validateFilePath(fp);
1278
+ allContents[path_1.default.basename(fp)] = await promises_1.default.readFile(fp, 'utf-8');
1279
+ }
1280
+ bodyObj[fieldName] = await promises_1.default.readFile(filePaths[0], 'utf-8');
1281
+ bodyObj.files = allContents;
1282
+ sourceLabel = `${filePaths.length} files`;
1283
+ }
1284
+ // Auto-populate filename if schema has it and user didn't provide it
1285
+ if (filePaths.length >= 1 && bodyObj.filename === undefined) {
1286
+ const schema = agentMeta.input_schema;
1287
+ const schemaProps = schema?.properties;
1288
+ if (schemaProps?.filename) {
1289
+ bodyObj.filename = path_1.default.basename(filePaths[0]);
1290
+ }
1291
+ }
1292
+ applySchemaDefaults(bodyObj, agentMeta.input_schema);
1293
+ if (llmCredentials)
1294
+ bodyObj.llm_credentials = llmCredentials;
1295
+ body = JSON.stringify(bodyObj);
1296
+ headers['Content-Type'] = 'application/json';
1297
+ }
1298
+ else {
1299
+ // Tool agents: send files as multipart, --data as metadata
1300
+ let metadata = resolvedBody;
1301
+ if (llmCredentials) {
1302
+ const metaObj = JSON.parse(metadata);
1303
+ metaObj.llm_credentials = llmCredentials;
1304
+ metadata = JSON.stringify(metaObj);
1305
+ }
1306
+ const multipart = await buildMultipartBody(filePaths, metadata);
1307
+ body = multipart.body;
1308
+ sourceLabel = multipart.sourceLabel;
1018
1309
  }
1310
+ }
1311
+ else if (options.data) {
1019
1312
  const resolvedBody = await resolveJsonBody(options.data);
1020
1313
  warnIfLocalPathReference(resolvedBody);
1021
1314
  if (llmCredentials) {
@@ -1040,6 +1333,7 @@ async function executeCloud(agentRef, file, options) {
1040
1333
  }
1041
1334
  }
1042
1335
  if (filePaths.length === 1) {
1336
+ await validateFilePath(filePaths[0]);
1043
1337
  const fileContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
1044
1338
  bodyObj[fieldName] = fileContent;
1045
1339
  sourceLabel = filePaths[0];
@@ -1047,6 +1341,7 @@ async function executeCloud(agentRef, file, options) {
1047
1341
  else if (filePaths.length > 1) {
1048
1342
  const allContents = {};
1049
1343
  for (const fp of filePaths) {
1344
+ await validateFilePath(fp);
1050
1345
  allContents[path_1.default.basename(fp)] = await promises_1.default.readFile(fp, 'utf-8');
1051
1346
  }
1052
1347
  const firstContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
@@ -1054,6 +1349,15 @@ async function executeCloud(agentRef, file, options) {
1054
1349
  bodyObj.files = allContents;
1055
1350
  sourceLabel = `${filePaths.length} files`;
1056
1351
  }
1352
+ // Auto-populate filename if schema has it and user didn't provide it
1353
+ if (filePaths.length >= 1 && bodyObj.filename === undefined) {
1354
+ const schema = agentMeta.input_schema;
1355
+ const schemaProps = schema?.properties;
1356
+ if (schemaProps?.filename) {
1357
+ bodyObj.filename = path_1.default.basename(filePaths[0]);
1358
+ }
1359
+ }
1360
+ applySchemaDefaults(bodyObj, agentMeta.input_schema);
1057
1361
  if (llmCredentials) {
1058
1362
  bodyObj.llm_credentials = llmCredentials;
1059
1363
  }
@@ -1139,6 +1443,12 @@ async function executeCloud(agentRef, file, options) {
1139
1443
  payload.message ||
1140
1444
  response.statusText
1141
1445
  : response.statusText;
1446
+ if (response.status >= 500) {
1447
+ spinner?.fail(`Server error (${response.status})`);
1448
+ throw new errors_1.CliError(`${message}\n\n` +
1449
+ `This is a server-side error. Try again in a moment.\n` +
1450
+ `If it persists, check the dashboard for run logs or try a different provider.`);
1451
+ }
1142
1452
  spinner?.fail(`Run failed: ${message}`);
1143
1453
  throw new errors_1.CliError(message);
1144
1454
  }
@@ -1413,7 +1723,12 @@ contains keys like 'path', 'directory', 'file', etc., those values will be inter
1413
1723
  by the server, not your local machine. To use local files, use --local or --file.
1414
1724
  `)
1415
1725
  .action(async (agentRef, file, options) => {
1416
- if (options.local) {
1726
+ if (options.local && isLocalPath(agentRef)) {
1727
+ // Local directory execution (e.g., orch run . --local)
1728
+ const args = file ? [file] : [];
1729
+ await executeLocalFromDir(agentRef, args, options);
1730
+ }
1731
+ else if (options.local) {
1417
1732
  // Local execution: file arg becomes first positional arg
1418
1733
  const args = file ? [file] : [];
1419
1734
  await executeLocal(agentRef, args, options);
@@ -48,6 +48,10 @@ const installed_1 = require("../lib/installed");
48
48
  const pricing_1 = require("../lib/pricing");
49
49
  const package_json_1 = __importDefault(require("../../package.json"));
50
50
  const DEFAULT_VERSION = 'latest';
51
+ function stripFrontmatter(content) {
52
+ const match = content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/);
53
+ return match ? match[1].trimStart() : content;
54
+ }
51
55
  /**
52
56
  * AI tool skill directories.
53
57
  *
@@ -189,7 +193,7 @@ async function downloadSkillWithFallback(config, org, skill, version) {
189
193
  }
190
194
  }
191
195
  throw new errors_1.CliError(`This skill is server-only and cannot be downloaded.\n\n` +
192
- `Skills are loaded automatically during server execution via 'orch run'.`);
196
+ `Skills are loaded automatically during server execution via 'orchagent run'.`);
193
197
  }
194
198
  // Free skill or public metadata available - proceed with normal download
195
199
  if (skillMeta) {
@@ -203,11 +207,11 @@ async function downloadSkillWithFallback(config, org, skill, version) {
203
207
  if (payload?.error?.code === 'PAID_AGENT_SERVER_ONLY') {
204
208
  const price = payload.error.price_per_call_cents || 0;
205
209
  throw new errors_1.CliError(`This skill costs $${(price / 100).toFixed(2)}/call and runs on server only.\n\n` +
206
- `Use: orch run ${org}/${skill}@${version} --data '{...}'`);
210
+ `Use: orchagent run ${org}/${skill}@${version} --data '{...}'`);
207
211
  }
208
212
  if (payload?.error?.code === 'DOWNLOAD_DISABLED') {
209
213
  throw new errors_1.CliError(`This skill is server-only and cannot be downloaded.\n\n` +
210
- `Skills are loaded automatically during server execution via 'orch run'.`);
214
+ `Skills are loaded automatically during server execution via 'orchagent run'.`);
211
215
  }
212
216
  }
213
217
  throw err;
@@ -443,6 +447,8 @@ Instructions and guidance for AI agents...
443
447
  // Determine scope (--global is legacy alias for --scope user; then config default; then 'project')
444
448
  const scope = options.global ? 'user' : (options.scope || await (0, config_1.getDefaultScope)() || 'project');
445
449
  result.scope = scope;
450
+ // Strip existing YAML frontmatter from the prompt to avoid duplication
451
+ const cleanPrompt = stripFrontmatter(skillData.prompt);
446
452
  // Build skill content with header
447
453
  const skillContent = `# ${skillData.name}
448
454
 
@@ -450,7 +456,7 @@ ${skillData.description || ''}
450
456
 
451
457
  ---
452
458
 
453
- ${skillData.prompt}
459
+ ${cleanPrompt}
454
460
  `;
455
461
  // Dry run - show what would be installed
456
462
  if (options.dryRun) {
@@ -42,7 +42,7 @@ function registerUpdateCommand(program) {
42
42
  const resolved = await (0, config_1.getResolvedConfig)();
43
43
  const installed = await (0, installed_1.getInstalled)();
44
44
  if (installed.length === 0) {
45
- process.stdout.write('No agents installed. Use "orch install <agent>" to install agents.\n');
45
+ process.stdout.write('No agents installed. Use "orchagent install <agent>" to install agents.\n');
46
46
  return;
47
47
  }
48
48
  // Filter to specific agent if provided
@@ -195,7 +195,7 @@ function registerUpdateCommand(program) {
195
195
  if (skippedMissing > 0) {
196
196
  process.stdout.write(`${skippedMissing} agent(s) have missing files.\n`);
197
197
  }
198
- process.stdout.write('Run "orch update" without --check to apply updates.\n');
198
+ process.stdout.write('Run "orchagent update" without --check to apply updates.\n');
199
199
  }
200
200
  else {
201
201
  process.stdout.write(`Applied ${updatesApplied} update(s).\n`);
@@ -35,7 +35,7 @@ async function checkApiKeyPresent() {
35
35
  name: 'api_key_present',
36
36
  status: 'error',
37
37
  message: 'No API key configured',
38
- fix: 'Run `orch login` or set ORCHAGENT_API_KEY environment variable',
38
+ fix: 'Run `orchagent login` or set ORCHAGENT_API_KEY environment variable',
39
39
  details: { configured: false },
40
40
  };
41
41
  }
@@ -75,7 +75,7 @@ async function checkApiKeyValid() {
75
75
  name: 'api_key_valid',
76
76
  status: 'error',
77
77
  message: 'API key is invalid or expired',
78
- fix: 'Run `orch login` to get a new key',
78
+ fix: 'Run `orchagent login` to get a new key',
79
79
  details: { error: err.message, status: err.status },
80
80
  };
81
81
  }
@@ -31,7 +31,7 @@ async function checkConfigExists() {
31
31
  name: 'config_exists',
32
32
  status: 'warning',
33
33
  message: 'Config file not found',
34
- fix: 'Run `orch login` to create config',
34
+ fix: 'Run `orchagent login` to create config',
35
35
  details: { path: CONFIG_PATH, exists: false },
36
36
  };
37
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.45",
3
+ "version": "0.3.47",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",
@@ -25,7 +25,6 @@
25
25
  "node": ">=18"
26
26
  },
27
27
  "bin": {
28
- "orchagent": "dist/index.js",
29
28
  "orch": "dist/index.js"
30
29
  },
31
30
  "files": [