@orchagent/cli 0.3.100 → 0.3.102

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.
@@ -162,6 +162,7 @@ function registerScheduleCommand(program) {
162
162
  .option('--alert-webhook <url>', 'Webhook URL to POST on failure (HTTPS required)')
163
163
  .option('--alert-on-failure-count <n>', 'Number of consecutive failures before alerting (default: 3)', parseInt)
164
164
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
165
+ .option('--json', 'Output as JSON')
165
166
  .action(async (agentArg, options) => {
166
167
  const config = await (0, config_1.getResolvedConfig)();
167
168
  if (!config.apiKey) {
@@ -217,6 +218,10 @@ function registerScheduleCommand(program) {
217
218
  body: JSON.stringify(body),
218
219
  headers: { 'Content-Type': 'application/json' },
219
220
  });
221
+ if (options.json) {
222
+ (0, output_1.printJson)(result);
223
+ return;
224
+ }
220
225
  const s = result.schedule;
221
226
  process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule created\n\n`);
222
227
  process.stdout.write(` ID: ${s.id}\n`);
@@ -264,6 +269,7 @@ function registerScheduleCommand(program) {
264
269
  .option('--alert-on-failure-count <n>', 'Number of consecutive failures before alerting', parseInt)
265
270
  .option('--clear-alert-webhook', 'Remove the alert webhook URL')
266
271
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
272
+ .option('--json', 'Output as JSON')
267
273
  .action(async (partialScheduleId, options) => {
268
274
  const config = await (0, config_1.getResolvedConfig)();
269
275
  if (!config.apiKey) {
@@ -322,6 +328,10 @@ function registerScheduleCommand(program) {
322
328
  body: JSON.stringify(updates),
323
329
  headers: { 'Content-Type': 'application/json' },
324
330
  });
331
+ if (options.json) {
332
+ (0, output_1.printJson)(result);
333
+ return;
334
+ }
325
335
  const s = result.schedule;
326
336
  process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule updated\n\n`);
327
337
  process.stdout.write(` ID: ${s.id}\n`);
@@ -349,6 +359,7 @@ function registerScheduleCommand(program) {
349
359
  .description('Delete a schedule')
350
360
  .option('-y, --yes', 'Skip confirmation prompt')
351
361
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
362
+ .option('--json', 'Output as JSON (implies --yes)')
352
363
  .action(async (partialScheduleId, options) => {
353
364
  const config = await (0, config_1.getResolvedConfig)();
354
365
  if (!config.apiKey) {
@@ -356,7 +367,7 @@ function registerScheduleCommand(program) {
356
367
  }
357
368
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
358
369
  const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
359
- if (!options.yes) {
370
+ if (!options.yes && !options.json) {
360
371
  const rl = promises_1.default.createInterface({
361
372
  input: process.stdin,
362
373
  output: process.stdout,
@@ -368,7 +379,11 @@ function registerScheduleCommand(program) {
368
379
  return;
369
380
  }
370
381
  }
371
- await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
382
+ const result = await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
383
+ if (options.json) {
384
+ (0, output_1.printJson)({ ...result, id: scheduleId });
385
+ return;
386
+ }
372
387
  process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule ${scheduleId} deleted\n`);
373
388
  });
374
389
  // orch schedule trigger <schedule-id>
@@ -378,6 +393,7 @@ function registerScheduleCommand(program) {
378
393
  .option('--data <json>', 'Override input data as JSON')
379
394
  .addOption(new commander_1.Option('--input <json>').hideHelp())
380
395
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
396
+ .option('--json', 'Output as JSON')
381
397
  .action(async (partialScheduleId, options) => {
382
398
  const config = await (0, config_1.getResolvedConfig)();
383
399
  if (!config.apiKey) {
@@ -396,11 +412,17 @@ function registerScheduleCommand(program) {
396
412
  throw new errors_1.CliError('Invalid JSON in --data');
397
413
  }
398
414
  }
399
- process.stdout.write('Triggering schedule...\n');
415
+ if (!options.json) {
416
+ process.stdout.write('Triggering schedule...\n');
417
+ }
400
418
  const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/trigger`, body ? {
401
419
  body,
402
420
  headers: { 'Content-Type': 'application/json' },
403
421
  } : {});
422
+ if (options.json) {
423
+ (0, output_1.printJson)(result);
424
+ return;
425
+ }
404
426
  // Status-aware header message
405
427
  const isAsync = result.status === 'queued' || result.status === 'deduplicated';
406
428
  if (isAsync) {
@@ -565,6 +587,7 @@ function registerScheduleCommand(program) {
565
587
  .command('test-alert <schedule-id>')
566
588
  .description('Send a test alert to the schedule\'s configured webhook URL')
567
589
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
590
+ .option('--json', 'Output as JSON')
568
591
  .action(async (partialScheduleId, options) => {
569
592
  const config = await (0, config_1.getResolvedConfig)();
570
593
  if (!config.apiKey) {
@@ -572,8 +595,14 @@ function registerScheduleCommand(program) {
572
595
  }
573
596
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
574
597
  const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
575
- process.stdout.write('Sending test alert...\n');
598
+ if (!options.json) {
599
+ process.stdout.write('Sending test alert...\n');
600
+ }
576
601
  const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/test-alert`);
602
+ if (options.json) {
603
+ (0, output_1.printJson)({ ...result, schedule_id: scheduleId });
604
+ return;
605
+ }
577
606
  if (result.success) {
578
607
  process.stdout.write(chalk_1.default.green('\u2713') + ' Test alert delivered successfully\n');
579
608
  }
@@ -587,6 +616,7 @@ function registerScheduleCommand(program) {
587
616
  .description('Regenerate the webhook secret (invalidates old URL)')
588
617
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
589
618
  .option('-y, --yes', 'Skip confirmation prompt')
619
+ .option('--json', 'Output as JSON (implies --yes)')
590
620
  .action(async (partialScheduleId, options) => {
591
621
  const config = await (0, config_1.getResolvedConfig)();
592
622
  if (!config.apiKey) {
@@ -594,7 +624,7 @@ function registerScheduleCommand(program) {
594
624
  }
595
625
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
596
626
  const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
597
- if (!options.yes) {
627
+ if (!options.yes && !options.json) {
598
628
  const rl = promises_1.default.createInterface({
599
629
  input: process.stdin,
600
630
  output: process.stdout,
@@ -609,6 +639,10 @@ function registerScheduleCommand(program) {
609
639
  }
610
640
  }
611
641
  const result = await (0, api_1.request)(config, 'POST', `/workspaces/${workspaceId}/schedules/${scheduleId}/regenerate-webhook`);
642
+ if (options.json) {
643
+ (0, output_1.printJson)({ ...result, schedule_id: scheduleId });
644
+ return;
645
+ }
612
646
  process.stdout.write(chalk_1.default.green('\u2713') + ' Webhook secret regenerated\n\n');
613
647
  process.stdout.write(` ${chalk_1.default.bold('New Webhook URL')} (save this — retrieve later with ${chalk_1.default.cyan('orch schedule info --reveal')}):\n`);
614
648
  process.stdout.write(` ${result.webhook_url}\n\n`);
@@ -11,26 +11,11 @@ const chalk_1 = __importDefault(require("chalk"));
11
11
  const config_1 = require("../lib/config");
12
12
  const api_1 = require("../lib/api");
13
13
  const errors_1 = require("../lib/errors");
14
+ const resolve_agent_1 = require("../lib/resolve-agent");
14
15
  const output_1 = require("../lib/output");
15
16
  const spinner_1 = require("../lib/spinner");
16
17
  const llm_1 = require("../lib/llm");
17
18
  const analytics_1 = require("../lib/analytics");
18
- const DEFAULT_VERSION = 'latest';
19
- function parseAgentRef(value) {
20
- const [ref, versionPart] = value.split('@');
21
- const version = versionPart?.trim() || DEFAULT_VERSION;
22
- const segments = ref.split('/');
23
- if (segments.length === 1) {
24
- return { agent: segments[0], version };
25
- }
26
- if (segments.length === 2) {
27
- return { org: segments[0], agent: segments[1], version };
28
- }
29
- if (segments.length === 3) {
30
- return { org: segments[0], agent: segments[1], version: segments[2] };
31
- }
32
- throw new errors_1.CliError('Invalid agent reference. Use org/agent/version or org/agent@version format.');
33
- }
34
19
  // Severity color mapping
35
20
  function severityColor(severity) {
36
21
  switch (severity.toLowerCase()) {
@@ -189,15 +174,8 @@ Examples:
189
174
  if (!resolved.apiKey) {
190
175
  throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
191
176
  }
192
- const parsed = parseAgentRef(agentRef);
193
- const configFile = await (0, config_1.loadConfig)();
194
- const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
195
- if (!org) {
196
- throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
197
- }
198
- const agentId = `${org}/${parsed.agent}/${parsed.version}`;
199
- // Resolve workspace context for the target org
200
- const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(resolved, org);
177
+ const { org, agent: agentName, version, workspaceId } = await (0, resolve_agent_1.resolveAgentContext)(agentRef, resolved);
178
+ const agentId = `${org}/${agentName}/${version}`;
201
179
  // Detect LLM key for the scan
202
180
  let llmKey;
203
181
  let llmProvider;
@@ -235,6 +213,12 @@ Examples:
235
213
  if (options.maxAttacks) {
236
214
  requestBody.max_attacks = options.maxAttacks;
237
215
  }
216
+ // Send provider preference so gateway can narrow vault key search
217
+ // (even when no local key is found, the gateway resolves from vault)
218
+ const effectiveProvider = llmProvider || options.provider;
219
+ if (effectiveProvider) {
220
+ requestBody.llm_provider = effectiveProvider;
221
+ }
238
222
  const url = `${resolved.apiUrl.replace(/\/$/, '')}/security/test`;
239
223
  // Make the API call with a spinner
240
224
  const spinner = (0, spinner_1.createSpinner)(`Scanning ${agentId} for vulnerabilities...`);
@@ -22,7 +22,7 @@ Schedule examples:
22
22
  orch schedule create org/my-job --cron "0 */6 * * *" # Every 6 hours
23
23
 
24
24
  Local test:
25
- echo '{}' | python main.py
25
+ echo '{}' | python3 main.py
26
26
  """
27
27
 
28
28
  import json
@@ -14,7 +14,7 @@ exports.TEMPLATE_MANIFEST = `{
14
14
  "type": "agent",
15
15
  "description": "Weekly GitHub activity summary delivered to Discord. Uses Claude to analyse commits, PRs, and issues — surfaces patterns, risks, and trends.",
16
16
  "runtime": {
17
- "command": "python main.py"
17
+ "command": "python3 main.py"
18
18
  },
19
19
  "required_secrets": [
20
20
  "ORCHAGENT_API_KEY",
@@ -640,7 +640,7 @@ function runEntrypointWithInput(agentDir, entrypoint, stdinData, verbose) {
640
640
  /**
641
641
  * Run fixture tests for code_runtime agents by executing the entrypoint
642
642
  * with fixture input as stdin and validating the JSON output.
643
- * Same interface as E2B: python main.py < input.json
643
+ * Same interface as E2B: python3 main.py < input.json
644
644
  */
645
645
  async function runCodeRuntimeFixtureTests(agentDir, fixtures, entrypoint, verbose) {
646
646
  process.stderr.write(chalk_1.default.blue('\nRunning fixture tests (code runtime)...\n\n'));
@@ -660,7 +660,7 @@ async function runCodeRuntimeFixtureTests(agentDir, fixtures, entrypoint, verbos
660
660
  throw new errors_1.CliError(`Invalid JSON in ${fixtureName}: ${e.message}`);
661
661
  }
662
662
  const fixture = validateFixture(parsed, fixturePath);
663
- // Run entrypoint with fixture input as stdin (same as E2B: python main.py < input.json)
663
+ // Run entrypoint with fixture input as stdin (same as E2B: python3 main.py < input.json)
664
664
  const inputJson = JSON.stringify(fixture.input);
665
665
  const result = await runEntrypointWithInput(agentDir, entrypoint, inputJson, verbose);
666
666
  if (result.code !== 0) {
@@ -8,6 +8,7 @@ const promises_1 = __importDefault(require("readline/promises"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const config_1 = require("../lib/config");
10
10
  const api_1 = require("../lib/api");
11
+ const agent_ref_1 = require("../lib/agent-ref");
11
12
  const errors_1 = require("../lib/errors");
12
13
  const analytics_1 = require("../lib/analytics");
13
14
  const output_1 = require("../lib/output");
@@ -34,7 +35,7 @@ async function promptText(message) {
34
35
  }
35
36
  function registerTransferCommand(program) {
36
37
  program
37
- .command('transfer <agent-name>')
38
+ .command('transfer <agent>')
38
39
  .description('Transfer an agent to another workspace')
39
40
  .requiredOption('--to <workspace-slug>', 'Target workspace slug')
40
41
  .option('-w, --workspace <workspace-slug>', 'Source workspace slug (defaults to active workspace)')
@@ -43,12 +44,13 @@ function registerTransferCommand(program) {
43
44
  .option('--json', 'Output result as JSON')
44
45
  .addHelpText('after', `
45
46
  Examples:
46
- orch transfer my-agent --to team-workspace # Transfer agent to another workspace
47
+ orch transfer my-agent --to team-workspace # Transfer by name
48
+ orch transfer my-org/my-agent --to team-workspace # Transfer using org/agent format
47
49
  orch transfer my-agent --to team-workspace --workspace my-team
48
50
  orch transfer my-agent --to team-workspace --dry-run # Preview transfer
49
51
  orch transfer my-agent --to team-workspace --yes # Skip confirmation
50
52
  `)
51
- .action(async (agentName, options) => {
53
+ .action(async (agentArg, options) => {
52
54
  const write = (message) => {
53
55
  if (!options.json)
54
56
  process.stdout.write(message);
@@ -58,6 +60,14 @@ Examples:
58
60
  if (!config.apiKey) {
59
61
  throw new errors_1.CliError('Not logged in. Run `orchagent login` first.');
60
62
  }
63
+ // Parse org/agent[@version] or bare agent name
64
+ const parsed = (0, agent_ref_1.parseAgentRef)(agentArg);
65
+ const agentName = parsed.agent;
66
+ // If org was provided via org/agent format, use it as source workspace
67
+ // --workspace flag takes precedence if both are given
68
+ if (parsed.org && options.workspace && parsed.org !== options.workspace) {
69
+ throw new errors_1.CliError(`Conflicting source workspace: '${parsed.org}' (from agent ref) vs '${options.workspace}' (from --workspace flag).`);
70
+ }
61
71
  write('Finding agent and workspaces...\n');
62
72
  // Fetch workspace list first (needed to resolve source/target IDs).
63
73
  const workspacesResponse = await (0, api_1.request)(config, 'GET', '/workspaces');
@@ -66,8 +76,8 @@ Examples:
66
76
  if (!targetWorkspace) {
67
77
  throw new errors_1.CliError(`Workspace '${options.to}' not found. Run \`orchagent workspace list\` to see available workspaces.`);
68
78
  }
69
- // Resolve source workspace (optional). If set, list agents from that workspace.
70
- const sourceWorkspaceSlug = options.workspace ?? configFile.workspace;
79
+ // Resolve source workspace: --workspace flag > org from ref > config workspace
80
+ const sourceWorkspaceSlug = options.workspace ?? parsed.org ?? configFile.workspace;
71
81
  const sourceWorkspace = sourceWorkspaceSlug
72
82
  ? workspacesResponse.workspaces.find((w) => w.slug === sourceWorkspaceSlug)
73
83
  : null;
@@ -27,9 +27,21 @@ function registerTreeCommand(program) {
27
27
  throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
28
28
  }
29
29
  const { agent, version } = parsed;
30
- // Resolve workspace context for team workspaces
31
- const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
32
- const tree = await (0, api_1.request)(config, 'GET', `/agents/${org}/${agent}/${version}/tree`, ...(workspaceId ? [{ headers: { 'X-Workspace-Id': workspaceId } }] : []));
30
+ // Public-first fallback: try public tree endpoint (works for any public
31
+ // agent regardless of caller context), then fall back to authenticated
32
+ // endpoint with workspace header for private agents. Matches the pattern
33
+ // used by info/fork/estimate commands. (T12-04)
34
+ let tree;
35
+ try {
36
+ tree = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/tree`);
37
+ }
38
+ catch (err) {
39
+ if (!(err instanceof api_1.ApiError) || err.status !== 404)
40
+ throw err;
41
+ // Public endpoint returned 404 — try authenticated endpoint for private agents
42
+ const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
43
+ tree = await (0, api_1.request)(config, 'GET', `/agents/${org}/${agent}/${version}/tree`, workspaceId ? { headers: { 'X-Workspace-Id': workspaceId } } : undefined);
44
+ }
33
45
  if (options.json) {
34
46
  console.log(JSON.stringify(tree, null, 2));
35
47
  return;
@@ -172,6 +172,7 @@ Options:
172
172
  const org = await (0, api_1.getOrg)(config, workspaceId);
173
173
  const depResults = await (0, publish_1.checkDependencies)(config, deps, org.slug, workspaceId);
174
174
  const notFound = depResults.filter(r => r.status === 'not_found');
175
+ const notFoundCrossOrg = depResults.filter(r => r.status === 'not_found_cross_org');
175
176
  const notCallable = depResults.filter(r => r.status === 'found_not_callable');
176
177
  if (notFound.length > 0) {
177
178
  for (const dep of notFound) {
@@ -181,6 +182,14 @@ Options:
181
182
  });
182
183
  }
183
184
  }
185
+ if (notFoundCrossOrg.length > 0) {
186
+ for (const dep of notFoundCrossOrg) {
187
+ serverIssues.push({
188
+ level: 'warning',
189
+ message: `Dependency not found (unpublished or not accessible from this workspace): ${dep.ref}`,
190
+ });
191
+ }
192
+ }
184
193
  if (notCallable.length > 0) {
185
194
  for (const dep of notCallable) {
186
195
  serverIssues.push({
@@ -137,18 +137,23 @@ async function createCodeBundle(sourceDir, outputPath, options = {}) {
137
137
  const output = (0, fs_1.createWriteStream)(outputPath);
138
138
  const archive = (0, archiver_1.default)('zip', { zlib: { level: 9 } });
139
139
  let fileCount = 0;
140
+ const files = [];
140
141
  output.on('close', () => {
141
142
  resolve({
142
143
  path: outputPath,
143
144
  sizeBytes: archive.pointer(),
144
145
  fileCount,
146
+ files,
145
147
  });
146
148
  });
147
149
  archive.on('error', (err) => {
148
150
  reject(err);
149
151
  });
150
- archive.on('entry', () => {
152
+ archive.on('entry', (entry) => {
151
153
  fileCount++;
154
+ if (entry.name) {
155
+ files.push(entry.name);
156
+ }
152
157
  });
153
158
  archive.pipe(output);
154
159
  // Add directory contents with exclusions
@@ -235,6 +240,7 @@ async function previewBundle(sourceDir, options = {}) {
235
240
  totalSizeBytes,
236
241
  entrypoint,
237
242
  excludePatterns,
243
+ files,
238
244
  };
239
245
  }
240
246
  /**
package/dist/lib/llm.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * Used by run, call, and skill commands.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.DEFAULT_MODELS = exports.PROVIDER_ENV_VARS = exports.LlmError = void 0;
9
+ exports.MODEL_PROVIDER_PATTERNS = exports.DEFAULT_MODELS = exports.PROVIDER_ENV_VARS = exports.LlmError = void 0;
10
10
  exports.isRateLimitError = isRateLimitError;
11
11
  exports.detectLlmKeyFromEnv = detectLlmKeyFromEnv;
12
12
  exports.detectLlmKey = detectLlmKey;
@@ -15,6 +15,8 @@ exports.buildPrompt = buildPrompt;
15
15
  exports.callLlm = callLlm;
16
16
  exports.callLlmWithFallback = callLlmWithFallback;
17
17
  exports.validateProvider = validateProvider;
18
+ exports.detectProviderFromModel = detectProviderFromModel;
19
+ exports.warnProviderModelMismatch = warnProviderModelMismatch;
18
20
  const errors_1 = require("./errors");
19
21
  const llm_errors_1 = require("./llm-errors");
20
22
  class LlmError extends errors_1.CliError {
@@ -265,3 +267,37 @@ function validateProvider(provider) {
265
267
  throw new errors_1.CliError(`Invalid provider: ${provider}. Valid: ${validProviders.join(', ')}`);
266
268
  }
267
269
  }
270
+ /**
271
+ * Model-name patterns for auto-detecting the LLM provider.
272
+ * Tested against the lowercased model string.
273
+ */
274
+ exports.MODEL_PROVIDER_PATTERNS = {
275
+ openai: /^(gpt-|o1-|o3-|o4-|davinci|text-)/,
276
+ anthropic: /^claude-/,
277
+ gemini: /^gemini-/,
278
+ ollama: /^(llama|mistral|deepseek|phi|qwen)/,
279
+ };
280
+ /**
281
+ * Auto-detect the LLM provider from a model name using prefix patterns.
282
+ * Returns the provider string if a match is found, or null if ambiguous/unknown.
283
+ */
284
+ function detectProviderFromModel(model) {
285
+ const modelLower = model.toLowerCase();
286
+ for (const [provider, pattern] of Object.entries(exports.MODEL_PROVIDER_PATTERNS)) {
287
+ if (pattern.test(modelLower)) {
288
+ return provider;
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+ /**
294
+ * Warn if a model name doesn't match the expected provider's pattern.
295
+ * Used when both --model and --provider are explicitly specified.
296
+ */
297
+ function warnProviderModelMismatch(model, provider) {
298
+ const modelLower = model.toLowerCase();
299
+ const expectedPattern = exports.MODEL_PROVIDER_PATTERNS[provider];
300
+ if (expectedPattern && !expectedPattern.test(modelLower)) {
301
+ process.stderr.write(`Warning: Model '${model}' may not be a ${provider} model.\n\n`);
302
+ }
303
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveOrg = resolveOrg;
4
+ exports.resolveAgentContext = resolveAgentContext;
5
+ const agent_ref_1 = require("./agent-ref");
6
+ const config_1 = require("./config");
7
+ const api_1 = require("./api");
8
+ const errors_1 = require("./errors");
9
+ /**
10
+ * Resolve the org from a parsed AgentRef using the standard fallback chain:
11
+ * 1. Explicit org from ref (org/agent@version)
12
+ * 2. Workspace from config file
13
+ * 3. defaultOrg from resolved config
14
+ *
15
+ * Throws CliError if no org can be determined.
16
+ */
17
+ async function resolveOrg(parsed, config, options) {
18
+ if (parsed.org)
19
+ return parsed.org;
20
+ const configFile = await (0, config_1.loadConfig)();
21
+ const org = configFile.workspace ?? config.defaultOrg;
22
+ if (!org) {
23
+ throw new errors_1.CliError(options?.missingOrgMessage ??
24
+ 'Missing org. Use org/agent format or set default org.');
25
+ }
26
+ return org;
27
+ }
28
+ /**
29
+ * Central agent-reference resolution pipeline used by all commands.
30
+ *
31
+ * Takes a raw agent reference string (e.g., "org/agent@v2", "agent", "agent@latest")
32
+ * and resolves it to a full context with org, agent name, version, and workspace ID.
33
+ *
34
+ * Resolution steps:
35
+ * 1. Parse the ref string into { org?, agent, version }
36
+ * 2. Resolve org via fallback chain: ref → config workspace → defaultOrg
37
+ * 3. Resolve workspace ID for team workspace context (optional)
38
+ *
39
+ * @example
40
+ * const ctx = await resolveAgentContext('acme/my-agent@v2', config)
41
+ * // ctx = { org: 'acme', agent: 'my-agent', version: 'v2', workspaceId: 'ws-123' }
42
+ *
43
+ * @example
44
+ * const ctx = await resolveAgentContext('my-agent', config)
45
+ * // ctx = { org: 'default-org', agent: 'my-agent', version: 'latest', workspaceId: undefined }
46
+ */
47
+ async function resolveAgentContext(agentRefString, config, options) {
48
+ const parsed = (0, agent_ref_1.parseAgentRef)(agentRefString);
49
+ const org = await resolveOrg(parsed, config, {
50
+ missingOrgMessage: options?.missingOrgMessage,
51
+ });
52
+ let workspaceId;
53
+ if (!options?.skipWorkspaceResolution) {
54
+ workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
55
+ }
56
+ return {
57
+ org,
58
+ agent: parsed.agent,
59
+ version: parsed.version,
60
+ workspaceId,
61
+ };
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.100",
3
+ "version": "0.3.102",
4
4
  "description": "Command-line interface for orchagent — deploy and run AI agents for your team",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",