@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.
- package/dist/commands/call.js +6 -6
- package/dist/commands/delete.js +1 -1
- package/dist/commands/env.js +2 -2
- package/dist/commands/info.js +19 -45
- package/dist/commands/install.js +1 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/publish.js +1 -1
- package/dist/commands/run.js +320 -5
- package/dist/commands/skill.js +10 -4
- package/dist/commands/update.js +2 -2
- package/dist/lib/doctor/checks/auth.js +2 -2
- package/dist/lib/doctor/checks/config.js +1 -1
- package/package.json +1 -2
package/dist/commands/call.js
CHANGED
|
@@ -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 '
|
|
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 "
|
|
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 '
|
|
18
|
-
`
|
|
19
|
-
`
|
|
20
|
-
`Replace '
|
|
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
|
}
|
package/dist/commands/delete.js
CHANGED
|
@@ -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 `
|
|
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
|
package/dist/commands/env.js
CHANGED
|
@@ -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 `
|
|
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:
|
|
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,
|
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/install.js
CHANGED
|
@@ -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 '
|
|
164
|
+
Use 'orchagent run' to execute paid agents.
|
|
165
165
|
`)
|
|
166
166
|
.action(async (agentArg, options) => {
|
|
167
167
|
const jsonMode = options.json === true;
|
package/dist/commands/list.js
CHANGED
|
@@ -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 '
|
|
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
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -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:
|
|
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`);
|
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) {
|
|
@@ -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 '
|
|
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
|
-
|
|
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
|
*
|
|
@@ -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 '
|
|
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:
|
|
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 '
|
|
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
|
-
${
|
|
459
|
+
${cleanPrompt}
|
|
454
460
|
`;
|
|
455
461
|
// Dry run - show what would be installed
|
|
456
462
|
if (options.dryRun) {
|
package/dist/commands/update.js
CHANGED
|
@@ -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 "
|
|
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 "
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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.
|
|
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": [
|