@orchagent/cli 0.3.24 → 0.3.26

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.
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -170,10 +203,32 @@ argument or --file option instead.
170
203
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
171
204
  }
172
205
  const parsed = parseAgentRef(agentRef);
173
- const org = parsed.org ?? resolved.defaultOrg;
206
+ const configFile = await (0, config_1.loadConfig)();
207
+ const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
174
208
  if (!org) {
175
209
  throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
176
210
  }
211
+ // First-use confirmation when workspace is active (and user didn't specify explicit org)
212
+ if (configFile.workspace && !configFile.workspace_confirmed && !parsed.org) {
213
+ // Only prompt in interactive mode (TTY)
214
+ if (process.stdin.isTTY) {
215
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
216
+ const rl = readline.createInterface({
217
+ input: process.stdin,
218
+ output: process.stderr,
219
+ });
220
+ const answer = await new Promise((resolve) => {
221
+ rl.question(`Using workspace: ${configFile.workspace}. Continue? [Y/n]: `, resolve);
222
+ });
223
+ rl.close();
224
+ if (answer.toLowerCase() === 'n') {
225
+ throw new errors_1.CliError('Cancelled. Use explicit org/agent or `orch workspace use <slug>`.');
226
+ }
227
+ // Save confirmation so we don't prompt again until workspace changes
228
+ configFile.workspace_confirmed = true;
229
+ await (0, config_1.saveConfig)(configFile);
230
+ }
231
+ }
177
232
  const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version);
178
233
  const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
179
234
  const headers = {
@@ -816,10 +816,32 @@ Note: Use 'run' for local execution, 'call' for server-side execution.
816
816
  }
817
817
  const resolved = await (0, config_1.getResolvedConfig)();
818
818
  const parsed = parseAgentRef(agentRef);
819
- const org = parsed.org ?? resolved.defaultOrg;
819
+ const configFile = await (0, config_1.loadConfig)();
820
+ const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
820
821
  if (!org) {
821
822
  throw new errors_1.CliError('Missing org. Use org/agent format.');
822
823
  }
824
+ // First-use confirmation when workspace is active (and user didn't specify explicit org)
825
+ if (configFile.workspace && !configFile.workspace_confirmed && !parsed.org) {
826
+ // Only prompt in interactive mode (TTY)
827
+ if (process.stdin.isTTY) {
828
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
829
+ const rl = readline.createInterface({
830
+ input: process.stdin,
831
+ output: process.stderr,
832
+ });
833
+ const answer = await new Promise((resolve) => {
834
+ rl.question(`Using workspace: ${configFile.workspace}. Continue? [Y/n]: `, resolve);
835
+ });
836
+ rl.close();
837
+ if (answer.toLowerCase() === 'n') {
838
+ throw new errors_1.CliError('Cancelled. Use explicit org/agent or `orch workspace use <slug>`.');
839
+ }
840
+ // Save confirmation so we don't prompt again until workspace changes
841
+ configFile.workspace_confirmed = true;
842
+ await (0, config_1.saveConfig)(configFile);
843
+ }
844
+ }
823
845
  // Download agent definition with spinner
824
846
  const agentData = await (0, spinner_1.withSpinner)(`Downloading ${org}/${parsed.agent}@${parsed.version}...`, async () => {
825
847
  try {
@@ -14,6 +14,27 @@ const chokidar_1 = __importDefault(require("chokidar"));
14
14
  const errors_1 = require("../lib/errors");
15
15
  const config_1 = require("../lib/config");
16
16
  const llm_1 = require("../lib/llm");
17
+ /**
18
+ * Validate a fixture and return helpful errors
19
+ */
20
+ function validateFixture(data, fixturePath) {
21
+ const fileName = path_1.default.basename(fixturePath);
22
+ if (typeof data !== 'object' || data === null) {
23
+ throw new errors_1.CliError(`Invalid fixture ${fileName}: must be a JSON object`);
24
+ }
25
+ const obj = data;
26
+ if (!obj.input || typeof obj.input !== 'object') {
27
+ throw new errors_1.CliError(`Invalid fixture ${fileName}: missing required "input" field.\n` +
28
+ ` Expected format: { "input": {...}, "expected_output": {...} }`);
29
+ }
30
+ if (!obj.expected_output && !obj.expected_contains) {
31
+ throw new errors_1.CliError(`Invalid fixture ${fileName}: must have "expected_output" or "expected_contains".\n` +
32
+ ` Add one of:\n` +
33
+ ` "expected_output": {"key": "exact value to match"}\n` +
34
+ ` "expected_contains": ["substring to find"]`);
35
+ }
36
+ return data;
37
+ }
17
38
  /**
18
39
  * Parse SKILL.md frontmatter
19
40
  */
@@ -70,8 +91,10 @@ function runCommand(command, args, cwd, verbose) {
70
91
  * Check if a command exists
71
92
  */
72
93
  async function commandExists(command) {
94
+ const isWindows = process.platform === 'win32';
95
+ const checker = isWindows ? 'where' : 'which';
73
96
  try {
74
- const proc = (0, child_process_1.spawn)('which', [command], { shell: true });
97
+ const proc = (0, child_process_1.spawn)(checker, [command], { shell: true, stdio: 'ignore' });
75
98
  return new Promise((resolve) => {
76
99
  proc.on('close', (code) => resolve(code === 0));
77
100
  proc.on('error', () => resolve(false));
@@ -204,22 +227,28 @@ async function discoverTests(agentDir) {
204
227
  */
205
228
  async function runPythonTests(agentDir, verbose) {
206
229
  process.stderr.write(chalk_1.default.blue('\nRunning Python tests...\n\n'));
207
- // Check if pytest is available
230
+ // Check if pytest is available directly
208
231
  const hasPytest = await commandExists('pytest');
209
- let command;
210
- let args;
211
232
  if (hasPytest) {
212
- command = 'pytest';
213
- args = verbose ? ['-v'] : [];
233
+ const args = verbose ? ['-v'] : [];
234
+ const { code } = await runCommand('pytest', args, agentDir, verbose);
235
+ return code;
214
236
  }
215
- else {
216
- command = 'python3';
217
- args = ['-m', 'pytest'];
218
- if (verbose)
219
- args.push('-v');
237
+ // Try Python commands in order of preference
238
+ const pythonCommands = process.platform === 'win32'
239
+ ? ['python', 'py', 'python3']
240
+ : ['python3', 'python'];
241
+ for (const pythonCmd of pythonCommands) {
242
+ if (await commandExists(pythonCmd)) {
243
+ const args = ['-m', 'pytest'];
244
+ if (verbose)
245
+ args.push('-v');
246
+ const { code } = await runCommand(pythonCmd, args, agentDir, verbose);
247
+ return code;
248
+ }
220
249
  }
221
- const { code } = await runCommand(command, args, agentDir, verbose);
222
- return code;
250
+ process.stderr.write(chalk_1.default.red('No Python interpreter found. Install Python and pytest.\n'));
251
+ return 1;
223
252
  }
224
253
  /**
225
254
  * Run JavaScript/TypeScript tests
@@ -292,8 +321,8 @@ async function runFixtureTests(agentDir, fixtures, verbose, config) {
292
321
  throw new errors_1.CliError('No LLM key found for fixture tests.\n' +
293
322
  'Set an environment variable (e.g., OPENAI_API_KEY) or run `orchagent keys add <provider>`');
294
323
  }
295
- const { provider, key } = detected;
296
- const model = (0, llm_1.getDefaultModel)(provider);
324
+ const { provider, key, model: serverModel } = detected;
325
+ const model = serverModel ?? (0, llm_1.getDefaultModel)(provider);
297
326
  let passed = 0;
298
327
  let failed = 0;
299
328
  for (const fixturePath of fixtures) {
@@ -301,7 +330,14 @@ async function runFixtureTests(agentDir, fixtures, verbose, config) {
301
330
  process.stderr.write(` ${fixtureName}: `);
302
331
  try {
303
332
  const raw = await promises_1.default.readFile(fixturePath, 'utf-8');
304
- const fixture = JSON.parse(raw);
333
+ let parsed;
334
+ try {
335
+ parsed = JSON.parse(raw);
336
+ }
337
+ catch (e) {
338
+ throw new errors_1.CliError(`Invalid JSON in ${path_1.default.basename(fixturePath)}: ${e.message}`);
339
+ }
340
+ const fixture = validateFixture(parsed, fixturePath);
305
341
  // Build and call LLM
306
342
  const fullPrompt = (0, llm_1.buildPrompt)(prompt, fixture.input);
307
343
  const result = await (0, llm_1.callLlm)(provider, key, model, fullPrompt, outputSchema);
@@ -85,6 +85,7 @@ async function useWorkspace(slug) {
85
85
  // Save to config
86
86
  const configFile = await (0, config_1.loadConfig)();
87
87
  configFile.workspace = slug;
88
+ delete configFile.workspace_confirmed;
88
89
  await (0, config_1.saveConfig)(configFile);
89
90
  await (0, analytics_1.track)('cli_workspace_use', { slug });
90
91
  process.stdout.write(chalk_1.default.green('\u2713') + ` Now using workspace: ${workspace.name} (${workspace.slug})\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.24",
3
+ "version": "0.3.26",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",