@orchagent/cli 0.3.45 → 0.3.46
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/info.js +19 -45
- package/dist/commands/run.js +319 -4
- package/dist/commands/skill.js +7 -1
- package/package.json +1 -1
package/dist/commands/info.js
CHANGED
|
@@ -49,49 +49,27 @@ async function fetchReadme(url) {
|
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
async function
|
|
53
|
-
//
|
|
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
|
|
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 };
|
package/dist/commands/run.js
CHANGED
|
@@ -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) {
|
|
@@ -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
|
-
|
|
1017
|
-
|
|
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);
|
package/dist/commands/skill.js
CHANGED
|
@@ -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
|
*
|
|
@@ -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
|
-
${
|
|
459
|
+
${cleanPrompt}
|
|
454
460
|
`;
|
|
455
461
|
// Dry run - show what would be installed
|
|
456
462
|
if (options.dryRun) {
|