@orchagent/cli 0.3.8 → 0.3.11

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.
@@ -13,6 +13,41 @@ const output_1 = require("../lib/output");
13
13
  const llm_1 = require("../lib/llm");
14
14
  const analytics_1 = require("../lib/analytics");
15
15
  const DEFAULT_VERSION = 'latest';
16
+ // Keys that might indicate local file path references in JSON payloads
17
+ const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
18
+ /**
19
+ * Check if a parsed JSON object contains keys that might reference local filesystem paths.
20
+ * Returns the first matching key found, or undefined if none found.
21
+ */
22
+ function findLocalPathKey(obj) {
23
+ if (typeof obj !== 'object' || obj === null) {
24
+ return undefined;
25
+ }
26
+ const keys = Object.keys(obj);
27
+ for (const key of keys) {
28
+ if (LOCAL_PATH_KEYS.includes(key.toLowerCase())) {
29
+ return key;
30
+ }
31
+ }
32
+ return undefined;
33
+ }
34
+ /**
35
+ * Emit a warning to stderr if the payload contains local path references.
36
+ */
37
+ function warnIfLocalPathReference(jsonBody) {
38
+ try {
39
+ const parsed = JSON.parse(jsonBody);
40
+ const pathKey = findLocalPathKey(parsed);
41
+ if (pathKey) {
42
+ process.stderr.write(`Warning: Your payload contains a local path reference ('${pathKey}').\n` +
43
+ `Remote agents cannot access your local filesystem. The path will be interpreted\n` +
44
+ `by the server, not your local machine. Use --help for more information.\n\n`);
45
+ }
46
+ }
47
+ catch {
48
+ // If parsing fails, skip the warning (the actual error will be thrown later)
49
+ }
50
+ }
16
51
  function parseAgentRef(value) {
17
52
  const [ref, versionPart] = value.split('@');
18
53
  const version = versionPart?.trim() || DEFAULT_VERSION;
@@ -118,6 +153,11 @@ Examples:
118
153
  orch call acme/image-processor photo.jpg --output result.png
119
154
 
120
155
  Note: Use 'call' for server-side execution (requires login), 'run' for local execution.
156
+
157
+ Important: Remote agents cannot access your local filesystem. If your --data payload
158
+ contains keys like 'path', 'directory', 'file', etc., those values will be interpreted
159
+ by the server, not your local machine. To send local files, use the positional file
160
+ argument or --file option instead.
121
161
  `)
122
162
  .action(async (agentRef, file, options) => {
123
163
  // Merge --input alias into --data
@@ -194,6 +234,8 @@ Note: Use 'call' for server-side execution (requires login), 'run' for local exe
194
234
  }
195
235
  // Parse JSON and inject llm_credentials if available
196
236
  const resolvedBody = await resolveJsonBody(options.data);
237
+ // Warn if payload contains local path references
238
+ warnIfLocalPathReference(resolvedBody);
197
239
  if (llmCredentials) {
198
240
  const bodyObj = JSON.parse(resolvedBody);
199
241
  bodyObj.llm_credentials = llmCredentials;
@@ -52,6 +52,13 @@ async function resolveWorkspaceId(config, slug) {
52
52
  const org = await (0, api_1.getOrg)(config);
53
53
  return org.id;
54
54
  }
55
+ // First check if the target slug matches the user's own org
56
+ // This avoids calling /workspaces which requires Clerk user identity
57
+ const org = await (0, api_1.getOrg)(config);
58
+ if (org.slug === targetSlug) {
59
+ return org.id;
60
+ }
61
+ // Only call /workspaces if accessing a different team workspace
55
62
  const response = await (0, api_1.request)(config, 'GET', '/workspaces');
56
63
  const workspace = response.workspaces.find((w) => w.slug === targetSlug);
57
64
  if (!workspace) {
@@ -26,6 +26,7 @@ const install_1 = require("./install");
26
26
  const formats_1 = require("./formats");
27
27
  const update_1 = require("./update");
28
28
  const env_1 = require("./env");
29
+ const list_1 = require("./list");
29
30
  function registerCommands(program) {
30
31
  (0, login_1.registerLoginCommand)(program);
31
32
  (0, whoami_1.registerWhoamiCommand)(program);
@@ -52,4 +53,5 @@ function registerCommands(program) {
52
53
  (0, formats_1.registerFormatsCommand)(program);
53
54
  (0, update_1.registerUpdateCommand)(program);
54
55
  (0, env_1.registerEnvCommand)(program);
56
+ (0, list_1.registerListCommand)(program);
55
57
  }
@@ -14,7 +14,7 @@ const analytics_1 = require("../lib/analytics");
14
14
  const adapters_1 = require("../adapters");
15
15
  const installed_1 = require("../lib/installed");
16
16
  const agents_md_utils_1 = require("../lib/agents-md-utils");
17
- const DEFAULT_VERSION = 'v1';
17
+ const DEFAULT_VERSION = 'latest';
18
18
  function parseAgentRef(value) {
19
19
  const [ref, versionPart] = value.split('@');
20
20
  const version = versionPart?.trim() || DEFAULT_VERSION;
@@ -95,7 +95,7 @@ function registerInstallCommand(program) {
95
95
  if (jsonMode) {
96
96
  result.errors.push('Missing org. Use org/agent format or set default org.');
97
97
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
98
- process.exit(1);
98
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
99
99
  }
100
100
  throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
101
101
  }
@@ -111,7 +111,7 @@ function registerInstallCommand(program) {
111
111
  if (jsonMode) {
112
112
  result.errors.push(errMsg);
113
113
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
114
- process.exit(1);
114
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
115
115
  }
116
116
  throw new errors_1.CliError(errMsg);
117
117
  }
@@ -134,7 +134,7 @@ function registerInstallCommand(program) {
134
134
  if (jsonMode) {
135
135
  result.errors.push(errMsg);
136
136
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
137
- process.exit(1);
137
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
138
138
  }
139
139
  throw new errors_1.CliError(errMsg);
140
140
  }
@@ -149,7 +149,7 @@ function registerInstallCommand(program) {
149
149
  if (jsonMode) {
150
150
  result.errors.push(err instanceof Error ? err.message : String(err));
151
151
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
152
- process.exit(1);
152
+ process.exit(errors_1.ExitCodes.NOT_FOUND);
153
153
  }
154
154
  throw err;
155
155
  }
@@ -262,11 +262,11 @@ function registerInstallCommand(program) {
262
262
  if (jsonMode) {
263
263
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
264
264
  if (!result.success) {
265
- process.exit(1);
265
+ process.exit(errors_1.ExitCodes.GENERAL_ERROR);
266
266
  }
267
267
  }
268
268
  else if (!result.success && !options.dryRun) {
269
- process.exit(1);
269
+ process.exit(errors_1.ExitCodes.GENERAL_ERROR);
270
270
  }
271
271
  });
272
272
  }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerListCommand = registerListCommand;
4
+ const installed_1 = require("../lib/installed");
5
+ const output_1 = require("../lib/output");
6
+ function registerListCommand(program) {
7
+ program
8
+ .command('list')
9
+ .alias('ls')
10
+ .description('List installed agents')
11
+ .option('--json', 'Output as JSON')
12
+ .action(async (options) => {
13
+ const installed = await (0, installed_1.getInstalled)();
14
+ if (options.json) {
15
+ (0, output_1.printJson)(installed);
16
+ return;
17
+ }
18
+ if (installed.length === 0) {
19
+ process.stdout.write('No agents installed.\n');
20
+ process.stdout.write('Install agents with: orch install <agent>\n');
21
+ return;
22
+ }
23
+ process.stdout.write('Installed agents:\n\n');
24
+ for (const agent of installed) {
25
+ process.stdout.write(` ${agent.agent}@${agent.version}\n`);
26
+ }
27
+ });
28
+ }
@@ -161,6 +161,7 @@ function registerPublishCommand(program) {
161
161
  .option('--dry-run', 'Show what would be published without making changes')
162
162
  .option('--skills <skills>', 'Default skills (comma-separated, e.g., org/skill@v1,org/other@v1)')
163
163
  .option('--skills-locked', 'Lock default skills (callers cannot override via headers)')
164
+ .option('--docker', 'Include Dockerfile for custom environment (builds E2B template)')
164
165
  .action(async (options) => {
165
166
  if (options.private) {
166
167
  process.stderr.write('Warning: --private is deprecated (private is now the default). You can safely remove it.\n');
@@ -449,10 +450,23 @@ function registerPublishCommand(program) {
449
450
  const tempDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'orchagent-bundle-'));
450
451
  const bundlePath = path_1.default.join(tempDir, 'bundle.zip');
451
452
  try {
453
+ // Build include patterns - add Dockerfile if --docker flag is set
454
+ const includePatterns = [...(manifest.bundle?.include || [])];
455
+ if (options.docker) {
456
+ const dockerfilePath = path_1.default.join(cwd, 'Dockerfile');
457
+ try {
458
+ await promises_1.default.access(dockerfilePath);
459
+ includePatterns.push('Dockerfile');
460
+ process.stdout.write(` Including Dockerfile for custom environment\n`);
461
+ }
462
+ catch {
463
+ throw new errors_1.CliError('--docker flag specified but no Dockerfile found in project directory');
464
+ }
465
+ }
452
466
  const bundleResult = await (0, bundle_1.createCodeBundle)(cwd, bundlePath, {
453
467
  entrypoint: manifest.entrypoint,
454
468
  exclude: manifest.bundle?.exclude,
455
- include: manifest.bundle?.include,
469
+ include: includePatterns.length > 0 ? includePatterns : undefined,
456
470
  });
457
471
  process.stdout.write(` Created bundle: ${bundleResult.fileCount} files, ${(bundleResult.sizeBytes / 1024).toFixed(1)}KB\n`);
458
472
  // Validate bundle size
@@ -153,6 +153,11 @@ async function checkDependencies(config, dependencies) {
153
153
  return results;
154
154
  }
155
155
  async function promptUserForDeps(depStatuses) {
156
+ // In non-interactive mode (CI, piped input), exit gracefully with helpful message
157
+ if (!process.stdin.isTTY) {
158
+ process.stderr.write('Non-interactive mode detected. Use --with-deps to include dependencies or --no-deps to skip them.\n');
159
+ return 'cancel';
160
+ }
156
161
  const readline = await Promise.resolve().then(() => __importStar(require('readline')));
157
162
  const rl = readline.createInterface({
158
163
  input: process.stdin,
@@ -12,7 +12,7 @@ const api_1 = require("../lib/api");
12
12
  const errors_1 = require("../lib/errors");
13
13
  const analytics_1 = require("../lib/analytics");
14
14
  const package_json_1 = __importDefault(require("../../package.json"));
15
- const DEFAULT_VERSION = 'v1';
15
+ const DEFAULT_VERSION = 'latest';
16
16
  /**
17
17
  * AI tool skill directories.
18
18
  *
@@ -21,7 +21,7 @@ const DEFAULT_VERSION = 'v1';
21
21
  *
22
22
  * TODO: Research and add more AI tool directories as the ecosystem evolves.
23
23
  * Known tools to research: Gemini CLI, Aider, OpenCode, Amp, Windsurf, Cline,
24
- * GitHub Copilot CLI, Codex CLI, Qwen Code, Kimi Code, etc.
24
+ * GitHub Copilot CLI, Qwen Code, Kimi Code, etc.
25
25
  *
26
26
  * References:
27
27
  * - https://github.com/gotalab/skillport
@@ -31,7 +31,6 @@ const DEFAULT_VERSION = 'v1';
31
31
  const AI_TOOL_SKILL_DIRS = [
32
32
  { name: 'Claude Code', projectPath: '.claude/skills', userPath: '.claude/skills' },
33
33
  { name: 'Cursor', projectPath: '.cursor/skills', userPath: '.cursor/skills' },
34
- { name: 'Codex', projectPath: '.codex/skills', userPath: '.codex/skills' },
35
34
  { name: 'Amp', projectPath: '.agents/skills', userPath: '.agents/skills' },
36
35
  { name: 'OpenCode', projectPath: '.opencode/skill', userPath: '.opencode/skill' },
37
36
  { name: 'Antigravity', projectPath: '.agent/skills', userPath: '.agent/skills' },
@@ -194,7 +193,7 @@ Instructions and guidance for AI agents...
194
193
  if (jsonMode) {
195
194
  result.errors.push(errMsg);
196
195
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
197
- process.exit(1);
196
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
198
197
  }
199
198
  throw new errors_1.CliError(errMsg);
200
199
  }
@@ -223,7 +222,7 @@ Instructions and guidance for AI agents...
223
222
  if (jsonMode) {
224
223
  result.errors.push(errMsg);
225
224
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
226
- process.exit(1);
225
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
227
226
  }
228
227
  throw new errors_1.CliError(errMsg);
229
228
  }
@@ -238,7 +237,7 @@ Instructions and guidance for AI agents...
238
237
  if (jsonMode) {
239
238
  result.errors.push(err instanceof Error ? err.message : String(err));
240
239
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
241
- process.exit(1);
240
+ process.exit(errors_1.ExitCodes.NOT_FOUND);
242
241
  }
243
242
  throw err;
244
243
  }
@@ -247,7 +246,7 @@ Instructions and guidance for AI agents...
247
246
  if (jsonMode) {
248
247
  result.errors.push(errMsg);
249
248
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
250
- process.exit(1);
249
+ process.exit(errors_1.ExitCodes.INVALID_INPUT);
251
250
  }
252
251
  throw new errors_1.CliError('Skill has no content.\n\n' +
253
252
  'The skill exists but has an empty prompt. This may be a publishing issue.\n' +
@@ -309,7 +308,7 @@ ${skillData.prompt}
309
308
  if (jsonMode) {
310
309
  result.errors.push(errMsg);
311
310
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
312
- process.exit(1);
311
+ process.exit(errors_1.ExitCodes.GENERAL_ERROR);
313
312
  }
314
313
  throw new errors_1.CliError(errMsg);
315
314
  }
@@ -110,7 +110,10 @@ const DEFAULT_EXCLUDES = [
110
110
  * @returns Promise that resolves with bundle metadata
111
111
  */
112
112
  async function createCodeBundle(sourceDir, outputPath, options = {}) {
113
- const excludePatterns = [...DEFAULT_EXCLUDES, ...(options.exclude || [])];
113
+ // Build exclude patterns, but remove any that are in the include list
114
+ const includeSet = new Set(options.include || []);
115
+ const excludePatterns = [...DEFAULT_EXCLUDES, ...(options.exclude || [])]
116
+ .filter(pattern => !includeSet.has(pattern));
114
117
  // Verify source directory exists
115
118
  const stat = await promises_1.default.stat(sourceDir);
116
119
  if (!stat.isDirectory()) {
@@ -50,12 +50,11 @@ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
50
50
  const CONFIG_PATH = path_1.default.join(CONFIG_DIR, 'config.json');
51
51
  const DEFAULT_API_URL = 'https://api.orchagent.io';
52
52
  // Valid format IDs for multi-format agent export
53
- exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', 'codex', 'amp', 'opencode', 'antigravity'];
53
+ exports.VALID_FORMAT_IDS = ['claude-code', 'cursor', 'amp', 'opencode', 'antigravity'];
54
54
  // Map format IDs to skill directories (used by skill install and agent install)
55
55
  exports.FORMAT_SKILL_DIRS = {
56
56
  'claude-code': { name: 'Claude Code', projectPath: '.claude/skills', userPath: '.claude/skills' },
57
57
  'cursor': { name: 'Cursor', projectPath: '.cursor/skills', userPath: '.cursor/skills' },
58
- 'codex': { name: 'Codex', projectPath: '.codex/skills', userPath: '.codex/skills' },
59
58
  'amp': { name: 'Amp', projectPath: '.agents/skills', userPath: '.agents/skills' },
60
59
  'opencode': { name: 'OpenCode', projectPath: '.opencode/skill', userPath: '.opencode/skill' },
61
60
  'antigravity': { name: 'Antigravity', projectPath: '.agent/skills', userPath: '.agent/skills' },
@@ -36,9 +36,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.NetworkError = exports.ExitCodes = exports.CliError = void 0;
37
37
  exports.formatError = formatError;
38
38
  exports.exitWithError = exitWithError;
39
+ exports.mapHttpStatusToExitCode = mapHttpStatusToExitCode;
39
40
  exports.jsonInputError = jsonInputError;
40
41
  const Sentry = __importStar(require("@sentry/node"));
41
42
  const analytics_1 = require("./analytics");
43
+ const api_1 = require("./api");
42
44
  class CliError extends Error {
43
45
  exitCode;
44
46
  cause;
@@ -73,6 +75,10 @@ async function exitWithError(err) {
73
75
  if (err instanceof CliError) {
74
76
  process.exit(err.exitCode);
75
77
  }
78
+ // Handle API errors with proper exit codes
79
+ if (err instanceof api_1.ApiError) {
80
+ process.exit(mapHttpStatusToExitCode(err.status));
81
+ }
76
82
  process.exit(1);
77
83
  }
78
84
  exports.ExitCodes = {
@@ -87,6 +93,22 @@ exports.ExitCodes = {
87
93
  SERVER_ERROR: 8,
88
94
  NETWORK_ERROR: 9,
89
95
  };
96
+ /**
97
+ * Map HTTP status codes to CLI exit codes.
98
+ */
99
+ function mapHttpStatusToExitCode(status) {
100
+ if (status === 401)
101
+ return exports.ExitCodes.AUTH_ERROR;
102
+ if (status === 403)
103
+ return exports.ExitCodes.PERMISSION_DENIED;
104
+ if (status === 404)
105
+ return exports.ExitCodes.NOT_FOUND;
106
+ if (status === 429)
107
+ return exports.ExitCodes.RATE_LIMITED;
108
+ if (status >= 500 && status <= 599)
109
+ return exports.ExitCodes.SERVER_ERROR;
110
+ return exports.ExitCodes.GENERAL_ERROR;
111
+ }
90
112
  class NetworkError extends CliError {
91
113
  constructor(url, cause) {
92
114
  const host = new URL(url).host;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.8",
3
+ "version": "0.3.11",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",