@odla-ai/cli 0.27.15 → 0.27.17

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/bin.js CHANGED
@@ -174,7 +174,7 @@ function absoluteEntryPath(entryPath) {
174
174
 
175
175
  // src/bin.ts
176
176
  var argv = process.argv.slice(2);
177
- requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-2RZFZRRT.js")).runCli()).catch((err) => {
177
+ requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-DCSVQAZ6.js")).runCli()).catch((err) => {
178
178
  console.error(redactSecrets(`odla-ai: ${err instanceof Error ? err.message : String(err)}`));
179
179
  process.exitCode = exitCodeFor(err);
180
180
  });
@@ -2577,6 +2577,63 @@ function printGroup(out, heading, items) {
2577
2577
  out.log("");
2578
2578
  }
2579
2579
 
2580
+ // src/ai-models.ts
2581
+ import { DEFAULT_CATALOG } from "@odla-ai/ai";
2582
+ async function aiModels(options = {}) {
2583
+ const cfg = await loadProjectConfig(options.configPath ?? "odla.config.mjs");
2584
+ const env = options.env ?? cfg.envs[0] ?? "dev";
2585
+ if (!cfg.envs.includes(env)) throw new Error(`ai models env "${env}" is not declared in config envs`);
2586
+ const url = new URL(`/registry/apps/${encodeURIComponent(cfg.app.id)}/public-config`, cfg.platformUrl);
2587
+ url.searchParams.set("env", env);
2588
+ const response2 = await (options.fetch ?? fetch)(url);
2589
+ if (!response2.ok) throw new Error(`read app AI models failed (${response2.status}): ${await safeText4(response2)}`);
2590
+ const body = await response2.json();
2591
+ if (!body.ai) throw new Error(`ai is not configured for ${cfg.app.id}/${env}`);
2592
+ const mode = body.ai.mode === "hosted" ? "hosted" : "byok";
2593
+ const defaultModel = typeof body.ai.model === "string" ? body.ai.model : void 0;
2594
+ let models;
2595
+ if (mode === "hosted") {
2596
+ if (body.ai.enabled !== true) throw new Error(`hosted ai is disabled for ${cfg.app.id}/${env}`);
2597
+ if (!Array.isArray(body.ai.models) || !body.ai.models.every(isModelSpec)) {
2598
+ throw new Error("platform returned an invalid hosted AI model catalog");
2599
+ }
2600
+ models = body.ai.models;
2601
+ } else {
2602
+ const provider = typeof body.ai.provider === "string" ? body.ai.provider : cfg.ai?.provider;
2603
+ if (!provider) throw new Error(`BYOK ai has no provider for ${cfg.app.id}/${env}`);
2604
+ models = Object.values(DEFAULT_CATALOG).filter((model) => model.provider === provider);
2605
+ }
2606
+ if (options.provider) models = models.filter((model) => model.provider === options.provider);
2607
+ models.sort((a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id));
2608
+ const out = options.stdout ?? console;
2609
+ if (options.json) {
2610
+ out.log(JSON.stringify({ appId: cfg.app.id, env, mode, defaultModel: defaultModel ?? null, models }, null, 2));
2611
+ return;
2612
+ }
2613
+ out.log("provider model default capabilities");
2614
+ for (const model of models) {
2615
+ out.log([model.provider, model.id, model.id === defaultModel ? "yes" : "", capabilityList(model)].join(" "));
2616
+ }
2617
+ }
2618
+ function capabilityList(model) {
2619
+ return [
2620
+ model.capabilities.imageIn ? "image" : "",
2621
+ model.capabilities.audioIn ? "audio" : "",
2622
+ model.capabilities.documentIn ? "document" : "",
2623
+ model.capabilities.toolUse ? "tools" : "",
2624
+ model.capabilities.thinking || model.capabilities.effort ? "reasoning" : "",
2625
+ model.capabilities.webSearch || model.superpowers?.webSearch ? "web-search" : ""
2626
+ ].filter(Boolean).join(",");
2627
+ }
2628
+ function isModelSpec(value2) {
2629
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return false;
2630
+ const model = value2;
2631
+ return typeof model.id === "string" && typeof model.nativeId === "string" && (model.provider === "anthropic" || model.provider === "openai" || model.provider === "google") && Boolean(model.capabilities) && typeof model.capabilities === "object";
2632
+ }
2633
+ async function safeText4(response2) {
2634
+ return (await response2.text().catch(() => "request failed")).slice(0, 300);
2635
+ }
2636
+
2580
2637
  // src/config-operation-command.ts
2581
2638
  import {
2582
2639
  AppsError,
@@ -2768,7 +2825,7 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
2768
2825
  });
2769
2826
  if (res.ok || res.status === 404) return;
2770
2827
  if (res.status === 403) {
2771
- const detail = await safeText4(res);
2828
+ const detail = await safeText5(res);
2772
2829
  const code = errorCode(detail);
2773
2830
  if (code === "human_session_required") {
2774
2831
  throw new Error(
@@ -2784,7 +2841,7 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
2784
2841
  `${env}: this credential lacks live app.manage authority for "${cfg.app.id}" (tenant ${tenantId}) \u2014 nothing was minted or written; run "odla-ai provision --request-grant --email <odla-account>" to open a fresh owner review. If the human account is not an owner, an existing owner must add it in signed-in Studio; an agent token cannot repair ownership`
2785
2842
  );
2786
2843
  }
2787
- throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText4(res)}`);
2844
+ throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
2788
2845
  }
2789
2846
  function errorCode(text2) {
2790
2847
  try {
@@ -2800,7 +2857,7 @@ async function postJson(doFetch, url, bearer, body) {
2800
2857
  headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
2801
2858
  body: JSON.stringify(body)
2802
2859
  });
2803
- if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText4(res)}`);
2860
+ if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText5(res)}`);
2804
2861
  }
2805
2862
  function normalizeClerkConfig(value2) {
2806
2863
  if (!value2) return null;
@@ -2813,7 +2870,7 @@ function normalizeClerkConfig(value2) {
2813
2870
  const publishableKey = envValue(cfg.publishableKey);
2814
2871
  return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
2815
2872
  }
2816
- async function safeText4(res) {
2873
+ async function safeText5(res) {
2817
2874
  try {
2818
2875
  return redactSecrets((await res.text()).slice(0, 500));
2819
2876
  } catch {
@@ -4583,7 +4640,7 @@ async function getJson(doFetch, url, bearer) {
4583
4640
  const res = await doFetch(url, {
4584
4641
  headers: bearer ? { authorization: `Bearer ${bearer}` } : void 0
4585
4642
  });
4586
- if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText5(res)}`);
4643
+ if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText6(res)}`);
4587
4644
  return res.json();
4588
4645
  }
4589
4646
  async function postJson2(doFetch, url, bearer, body) {
@@ -4592,7 +4649,7 @@ async function postJson2(doFetch, url, bearer, body) {
4592
4649
  headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
4593
4650
  body: JSON.stringify(body)
4594
4651
  });
4595
- if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText5(res)}`);
4652
+ if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText6(res)}`);
4596
4653
  return res.json();
4597
4654
  }
4598
4655
  function publicConfigUrl(platformUrl, appId, env) {
@@ -4600,7 +4657,7 @@ function publicConfigUrl(platformUrl, appId, env) {
4600
4657
  url.searchParams.set("env", env);
4601
4658
  return url.toString();
4602
4659
  }
4603
- async function safeText5(res) {
4660
+ async function safeText6(res) {
4604
4661
  try {
4605
4662
  return redactSecrets((await res.text()).slice(0, 500));
4606
4663
  } catch {
@@ -4656,6 +4713,20 @@ async function secretsCommand(parsed, deps) {
4656
4713
  });
4657
4714
  }
4658
4715
  async function projectCommand(command, parsed, deps) {
4716
+ if (command === "ai") {
4717
+ const sub = parsed.positionals[1];
4718
+ if (sub !== "models") throw new Error(`unknown ai subcommand "${sub ?? ""}". Try "odla-ai ai models --env dev".`);
4719
+ assertArgs(parsed, ["config", "env", "provider", "json"], 2);
4720
+ await aiModels({
4721
+ configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
4722
+ env: stringOpt(parsed.options.env),
4723
+ provider: stringOpt(parsed.options.provider),
4724
+ json: parsed.options.json === true,
4725
+ fetch: deps.fetch,
4726
+ stdout: deps.stdout
4727
+ });
4728
+ return true;
4729
+ }
4659
4730
  if (command === "config") {
4660
4731
  const sub = parsed.positionals[1];
4661
4732
  if (sub !== "diff" && sub !== "plan" && sub !== "apply") {
@@ -8432,6 +8503,7 @@ Usage:
8432
8503
  odla-ai setup [--dir <project>] [--agent <name>] [--global] [--force]
8433
8504
  odla-ai init --app-id <id> --name <name> [--services db,ai,o11y,calendar] [--env dev --env prod] [--ai-provider <byok-provider>]
8434
8505
  odla-ai doctor [--config odla.config.mjs]
8506
+ odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
8435
8507
  odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
8436
8508
  odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
8437
8509
  odla-ai operations get <operation-id> [--json]
@@ -10535,14 +10607,14 @@ async function mintDbKey(opts, tenantId) {
10535
10607
  appId: tenantId
10536
10608
  })
10537
10609
  });
10538
- if (!created.ok) throw new Error(`db app create (${tenantId}) failed: ${created.status} ${await safeText6(created)}`);
10610
+ if (!created.ok) throw new Error(`db app create (${tenantId}) failed: ${created.status} ${await safeText7(created)}`);
10539
10611
  res = await opts.fetch(`${opts.cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/keys`, {
10540
10612
  method: "POST",
10541
10613
  headers,
10542
10614
  body: "{}"
10543
10615
  });
10544
10616
  }
10545
- if (!res.ok) throw new Error(`db key mint (${tenantId}) failed: ${res.status} ${await safeText6(res)}`);
10617
+ if (!res.ok) throw new Error(`db key mint (${tenantId}) failed: ${res.status} ${await safeText7(res)}`);
10546
10618
  const body = await res.json();
10547
10619
  if (!body.key) throw new Error(`db key mint (${tenantId}) returned no key`);
10548
10620
  return body.key;
@@ -10558,12 +10630,12 @@ async function issueO11yToken(opts) {
10558
10630
  `o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --rotate-o11y-token --push-secrets" to replace it explicitly`
10559
10631
  );
10560
10632
  }
10561
- if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText6(res)}`);
10633
+ if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText7(res)}`);
10562
10634
  const body = await res.json();
10563
10635
  if (!body.token) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) returned no token`);
10564
10636
  return body.token;
10565
10637
  }
10566
- async function safeText6(res) {
10638
+ async function safeText7(res) {
10567
10639
  try {
10568
10640
  return redactSecrets((await res.text()).slice(0, 500));
10569
10641
  } catch {
@@ -10818,6 +10890,7 @@ var PM_ENTITIES = {
10818
10890
  };
10819
10891
  var COMMAND_SURFACE = {
10820
10892
  agent: { jobs: {}, retry: {} },
10893
+ ai: { models: {} },
10821
10894
  admin: {
10822
10895
  ai: {
10823
10896
  show: {},
@@ -12793,6 +12866,7 @@ export {
12793
12866
  calendarDisconnect,
12794
12867
  CAPABILITIES,
12795
12868
  printCapabilities,
12869
+ aiModels,
12796
12870
  ConfigOperationCommandError,
12797
12871
  desiredRegistryState,
12798
12872
  configApply,
@@ -12837,4 +12911,4 @@ export {
12837
12911
  isTerminalHostedSecurityStatus,
12838
12912
  runCli
12839
12913
  };
12840
- //# sourceMappingURL=chunk-MR5QXX3B.js.map
12914
+ //# sourceMappingURL=chunk-3WVVHH3Y.js.map