@odla-ai/cli 0.14.0 → 0.14.1

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/index.cjs CHANGED
@@ -4671,7 +4671,7 @@ function hostedSecurityCredential(value) {
4671
4671
  // src/security-hosted-github.ts
4672
4672
  async function connectGitHubSecuritySource(options) {
4673
4673
  const out = options.stdout ?? console;
4674
- const repository = githubRepositoryName(options.repository);
4674
+ const repository = options.repository === void 0 ? void 0 : githubRepositoryName(options.repository);
4675
4675
  const attempt = await requestHostedSecurityJson(
4676
4676
  options,
4677
4677
  "/registry/github/connect",
@@ -4680,7 +4680,7 @@ async function connectGitHubSecuritySource(options) {
4680
4680
  body: JSON.stringify({
4681
4681
  appId: hostedIdentifier(options.appId, "appId"),
4682
4682
  env: hostedIdentifier(options.env, "env"),
4683
- repository
4683
+ ...repository === void 0 ? {} : { repository }
4684
4684
  })
4685
4685
  },
4686
4686
  "start GitHub connection"
@@ -5493,7 +5493,7 @@ function relativeDisplay(path, rootDir) {
5493
5493
  }
5494
5494
 
5495
5495
  // src/provision.ts
5496
- var import_apps2 = require("@odla-ai/apps");
5496
+ var import_apps3 = require("@odla-ai/apps");
5497
5497
  var import_ai2 = require("@odla-ai/ai");
5498
5498
  var import_node_process7 = __toESM(require("process"), 1);
5499
5499
 
@@ -5646,6 +5646,7 @@ async function safeText3(res) {
5646
5646
 
5647
5647
  // src/provision-helpers.ts
5648
5648
  var import_ai = require("@odla-ai/ai");
5649
+ var import_apps2 = require("@odla-ai/apps");
5649
5650
  function serviceRank(service) {
5650
5651
  if (service === "db") return 0;
5651
5652
  if (service === "calendar") return 2;
@@ -5655,6 +5656,54 @@ function defaultSecretName(provider) {
5655
5656
  const names = import_ai.DEFAULT_SECRET_NAMES;
5656
5657
  return names[provider] ?? `${provider}_api_key`;
5657
5658
  }
5659
+ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
5660
+ const tenantId = (0, import_apps2.tenantIdFor)(cfg.app.id, env);
5661
+ const res = await doFetch(`${cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/entitlements`, {
5662
+ headers: { authorization: `Bearer ${token}` }
5663
+ });
5664
+ if (res.ok || res.status === 404) return;
5665
+ if (res.status === 403) {
5666
+ throw new Error(
5667
+ `${env}: you are not an owner of "${cfg.app.id}" (tenant ${tenantId}) \u2014 nothing was minted or written; ask an existing owner to run "odla-ai app owners add <your-email>", then re-run provision`
5668
+ );
5669
+ }
5670
+ throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText4(res)}`);
5671
+ }
5672
+ async function readRegistryApp(cfg, token, doFetch) {
5673
+ const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
5674
+ headers: { authorization: `Bearer ${token}` }
5675
+ });
5676
+ if (res.status === 404) return null;
5677
+ if (!res.ok) return null;
5678
+ const json2 = await res.json();
5679
+ return json2.app ?? null;
5680
+ }
5681
+ async function postJson2(doFetch, url, bearer, body) {
5682
+ const res = await doFetch(url, {
5683
+ method: "POST",
5684
+ headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
5685
+ body: JSON.stringify(body)
5686
+ });
5687
+ if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText4(res)}`);
5688
+ }
5689
+ function normalizeClerkConfig(value) {
5690
+ if (!value) return null;
5691
+ if (typeof value === "string") {
5692
+ const publishableKey2 = envValue(value);
5693
+ return publishableKey2 ? { publishableKey: publishableKey2 } : null;
5694
+ }
5695
+ if (typeof value !== "object") return null;
5696
+ const cfg = value;
5697
+ const publishableKey = envValue(cfg.publishableKey);
5698
+ return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
5699
+ }
5700
+ async function safeText4(res) {
5701
+ try {
5702
+ return redactSecrets((await res.text()).slice(0, 500));
5703
+ } catch {
5704
+ return "";
5705
+ }
5706
+ }
5658
5707
 
5659
5708
  // src/secrets.ts
5660
5709
  var PROD_ENV_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
@@ -5799,7 +5848,7 @@ async function provision(options) {
5799
5848
  }
5800
5849
  const doFetch = options.fetch ?? fetch;
5801
5850
  const token = await getDeveloperToken(cfg, options, doFetch, out);
5802
- const apps = (0, import_apps2.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
5851
+ const apps = (0, import_apps3.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
5803
5852
  const existing = await readRegistryApp(cfg, token, doFetch);
5804
5853
  if (existing) {
5805
5854
  out.log(`app: ${cfg.app.id} already exists`);
@@ -5807,6 +5856,9 @@ async function provision(options) {
5807
5856
  await apps.createApp({ name: cfg.app.name, appId: cfg.app.id });
5808
5857
  out.log(`app: created ${cfg.app.id}`);
5809
5858
  }
5859
+ for (const env of cfg.envs) {
5860
+ await assertTenantAdminAccess(doFetch, cfg, env, token);
5861
+ }
5810
5862
  const serviceOrder = [...cfg.services].sort((a, b) => serviceRank(a) - serviceRank(b));
5811
5863
  for (const env of cfg.envs) {
5812
5864
  for (const service of serviceOrder) {
@@ -5840,7 +5892,7 @@ async function provision(options) {
5840
5892
  }
5841
5893
  }
5842
5894
  for (const env of cfg.envs) {
5843
- const tenantId = (0, import_apps2.tenantIdFor)(cfg.app.id, env);
5895
+ const tenantId = (0, import_apps3.tenantIdFor)(cfg.app.id, env);
5844
5896
  credentials = await provisionEnvCredentials({
5845
5897
  cfg,
5846
5898
  env,
@@ -5923,45 +5975,10 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
5923
5975
  }
5924
5976
  }
5925
5977
  }
5926
- async function readRegistryApp(cfg, token, doFetch) {
5927
- const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
5928
- headers: { authorization: `Bearer ${token}` }
5929
- });
5930
- if (res.status === 404) return null;
5931
- if (!res.ok) return null;
5932
- const json2 = await res.json();
5933
- return json2.app ?? null;
5934
- }
5935
- async function postJson2(doFetch, url, bearer, body) {
5936
- const res = await doFetch(url, {
5937
- method: "POST",
5938
- headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
5939
- body: JSON.stringify(body)
5940
- });
5941
- if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText4(res)}`);
5942
- }
5943
- function normalizeClerkConfig(value) {
5944
- if (!value) return null;
5945
- if (typeof value === "string") {
5946
- const publishableKey2 = envValue(value);
5947
- return publishableKey2 ? { publishableKey: publishableKey2 } : null;
5948
- }
5949
- if (typeof value !== "object") return null;
5950
- const cfg = value;
5951
- const publishableKey = envValue(cfg.publishableKey);
5952
- return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
5953
- }
5954
- async function safeText4(res) {
5955
- try {
5956
- return redactSecrets((await res.text()).slice(0, 500));
5957
- } catch {
5958
- return "";
5959
- }
5960
- }
5961
5978
 
5962
5979
  // src/secrets-set.ts
5963
5980
  var import_ai3 = require("@odla-ai/ai");
5964
- var import_apps3 = require("@odla-ai/apps");
5981
+ var import_apps4 = require("@odla-ai/apps");
5965
5982
  var PROD_ENV_NAMES2 = /* @__PURE__ */ new Set(["prod", "production"]);
5966
5983
  async function secretsSet(options) {
5967
5984
  const name = (options.name ?? "").trim();
@@ -6013,7 +6030,7 @@ async function resolveVaultWrite(options) {
6013
6030
  throw new Error(`refusing to store a secret for "${options.env}" without --yes`);
6014
6031
  }
6015
6032
  const value = await secretInputValue(options, "secret");
6016
- return { cfg, tenantId: (0, import_apps3.tenantIdFor)(cfg.app.id, options.env), value, doFetch, out };
6033
+ return { cfg, tenantId: (0, import_apps4.tenantIdFor)(cfg.app.id, options.env), value, doFetch, out };
6017
6034
  }
6018
6035
 
6019
6036
  // src/security-command-context.ts
@@ -6665,16 +6682,16 @@ async function githubSecurityCommand(parsed, dependencies) {
6665
6682
  }
6666
6683
  assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
6667
6684
  const context = await hostedSecurityContext(parsed, dependencies);
6668
- const repository = stringOpt(parsed.options.repo) ?? await inferGitHubRepository(process.cwd(), dependencies.readGitOrigin);
6685
+ const repository = stringOpt(parsed.options.repo) ?? await inferGitHubRepository(process.cwd(), dependencies.readGitOrigin).catch(() => void 0);
6669
6686
  const connection = await connectGitHubSecuritySource({
6670
6687
  ...context,
6671
- repository,
6688
+ ...repository === void 0 ? {} : { repository },
6672
6689
  open: parsed.options.open !== false,
6673
6690
  openInstallUrl: dependencies.openUrl ?? openUrl,
6674
6691
  wait: dependencies.pollWait,
6675
6692
  stdout: context.stdout
6676
6693
  });
6677
- context.stdout.log(`github: connected ${connection.repository ?? repository} (${connection.sourceId ?? "source pending"})`);
6694
+ context.stdout.log(`github: connected ${connection.repository ?? repository ?? "app repository"} (${connection.sourceId ?? "source pending"})`);
6678
6695
  context.stdout.log("github: odla.ai stores the installation; no PAT or GitHub token is written locally");
6679
6696
  }
6680
6697
  async function listSecuritySources(parsed, dependencies) {