@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/bin.cjs CHANGED
@@ -4618,7 +4618,7 @@ function hostedSecurityCredential(value) {
4618
4618
  // src/security-hosted-github.ts
4619
4619
  async function connectGitHubSecuritySource(options) {
4620
4620
  const out = options.stdout ?? console;
4621
- const repository = githubRepositoryName(options.repository);
4621
+ const repository = options.repository === void 0 ? void 0 : githubRepositoryName(options.repository);
4622
4622
  const attempt = await requestHostedSecurityJson(
4623
4623
  options,
4624
4624
  "/registry/github/connect",
@@ -4627,7 +4627,7 @@ async function connectGitHubSecuritySource(options) {
4627
4627
  body: JSON.stringify({
4628
4628
  appId: hostedIdentifier(options.appId, "appId"),
4629
4629
  env: hostedIdentifier(options.env, "env"),
4630
- repository
4630
+ ...repository === void 0 ? {} : { repository }
4631
4631
  })
4632
4632
  },
4633
4633
  "start GitHub connection"
@@ -5440,7 +5440,7 @@ function relativeDisplay(path, rootDir) {
5440
5440
  }
5441
5441
 
5442
5442
  // src/provision.ts
5443
- var import_apps2 = require("@odla-ai/apps");
5443
+ var import_apps3 = require("@odla-ai/apps");
5444
5444
  var import_ai2 = require("@odla-ai/ai");
5445
5445
  var import_node_process7 = __toESM(require("process"), 1);
5446
5446
 
@@ -5593,6 +5593,7 @@ async function safeText3(res) {
5593
5593
 
5594
5594
  // src/provision-helpers.ts
5595
5595
  var import_ai = require("@odla-ai/ai");
5596
+ var import_apps2 = require("@odla-ai/apps");
5596
5597
  function serviceRank(service) {
5597
5598
  if (service === "db") return 0;
5598
5599
  if (service === "calendar") return 2;
@@ -5602,6 +5603,54 @@ function defaultSecretName(provider) {
5602
5603
  const names = import_ai.DEFAULT_SECRET_NAMES;
5603
5604
  return names[provider] ?? `${provider}_api_key`;
5604
5605
  }
5606
+ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
5607
+ const tenantId = (0, import_apps2.tenantIdFor)(cfg.app.id, env);
5608
+ const res = await doFetch(`${cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/entitlements`, {
5609
+ headers: { authorization: `Bearer ${token}` }
5610
+ });
5611
+ if (res.ok || res.status === 404) return;
5612
+ if (res.status === 403) {
5613
+ throw new Error(
5614
+ `${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`
5615
+ );
5616
+ }
5617
+ throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText4(res)}`);
5618
+ }
5619
+ async function readRegistryApp(cfg, token, doFetch) {
5620
+ const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
5621
+ headers: { authorization: `Bearer ${token}` }
5622
+ });
5623
+ if (res.status === 404) return null;
5624
+ if (!res.ok) return null;
5625
+ const json2 = await res.json();
5626
+ return json2.app ?? null;
5627
+ }
5628
+ async function postJson2(doFetch, url, bearer, body) {
5629
+ const res = await doFetch(url, {
5630
+ method: "POST",
5631
+ headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
5632
+ body: JSON.stringify(body)
5633
+ });
5634
+ if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText4(res)}`);
5635
+ }
5636
+ function normalizeClerkConfig(value) {
5637
+ if (!value) return null;
5638
+ if (typeof value === "string") {
5639
+ const publishableKey2 = envValue(value);
5640
+ return publishableKey2 ? { publishableKey: publishableKey2 } : null;
5641
+ }
5642
+ if (typeof value !== "object") return null;
5643
+ const cfg = value;
5644
+ const publishableKey = envValue(cfg.publishableKey);
5645
+ return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
5646
+ }
5647
+ async function safeText4(res) {
5648
+ try {
5649
+ return redactSecrets((await res.text()).slice(0, 500));
5650
+ } catch {
5651
+ return "";
5652
+ }
5653
+ }
5605
5654
 
5606
5655
  // src/secrets.ts
5607
5656
  var PROD_ENV_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
@@ -5746,7 +5795,7 @@ async function provision(options) {
5746
5795
  }
5747
5796
  const doFetch = options.fetch ?? fetch;
5748
5797
  const token = await getDeveloperToken(cfg, options, doFetch, out);
5749
- const apps = (0, import_apps2.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
5798
+ const apps = (0, import_apps3.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
5750
5799
  const existing = await readRegistryApp(cfg, token, doFetch);
5751
5800
  if (existing) {
5752
5801
  out.log(`app: ${cfg.app.id} already exists`);
@@ -5754,6 +5803,9 @@ async function provision(options) {
5754
5803
  await apps.createApp({ name: cfg.app.name, appId: cfg.app.id });
5755
5804
  out.log(`app: created ${cfg.app.id}`);
5756
5805
  }
5806
+ for (const env of cfg.envs) {
5807
+ await assertTenantAdminAccess(doFetch, cfg, env, token);
5808
+ }
5757
5809
  const serviceOrder = [...cfg.services].sort((a, b) => serviceRank(a) - serviceRank(b));
5758
5810
  for (const env of cfg.envs) {
5759
5811
  for (const service of serviceOrder) {
@@ -5787,7 +5839,7 @@ async function provision(options) {
5787
5839
  }
5788
5840
  }
5789
5841
  for (const env of cfg.envs) {
5790
- const tenantId = (0, import_apps2.tenantIdFor)(cfg.app.id, env);
5842
+ const tenantId = (0, import_apps3.tenantIdFor)(cfg.app.id, env);
5791
5843
  credentials = await provisionEnvCredentials({
5792
5844
  cfg,
5793
5845
  env,
@@ -5870,45 +5922,10 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
5870
5922
  }
5871
5923
  }
5872
5924
  }
5873
- async function readRegistryApp(cfg, token, doFetch) {
5874
- const res = await doFetch(`${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}`, {
5875
- headers: { authorization: `Bearer ${token}` }
5876
- });
5877
- if (res.status === 404) return null;
5878
- if (!res.ok) return null;
5879
- const json2 = await res.json();
5880
- return json2.app ?? null;
5881
- }
5882
- async function postJson2(doFetch, url, bearer, body) {
5883
- const res = await doFetch(url, {
5884
- method: "POST",
5885
- headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
5886
- body: JSON.stringify(body)
5887
- });
5888
- if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText4(res)}`);
5889
- }
5890
- function normalizeClerkConfig(value) {
5891
- if (!value) return null;
5892
- if (typeof value === "string") {
5893
- const publishableKey2 = envValue(value);
5894
- return publishableKey2 ? { publishableKey: publishableKey2 } : null;
5895
- }
5896
- if (typeof value !== "object") return null;
5897
- const cfg = value;
5898
- const publishableKey = envValue(cfg.publishableKey);
5899
- return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
5900
- }
5901
- async function safeText4(res) {
5902
- try {
5903
- return redactSecrets((await res.text()).slice(0, 500));
5904
- } catch {
5905
- return "";
5906
- }
5907
- }
5908
5925
 
5909
5926
  // src/secrets-set.ts
5910
5927
  var import_ai3 = require("@odla-ai/ai");
5911
- var import_apps3 = require("@odla-ai/apps");
5928
+ var import_apps4 = require("@odla-ai/apps");
5912
5929
  var PROD_ENV_NAMES2 = /* @__PURE__ */ new Set(["prod", "production"]);
5913
5930
  async function secretsSet(options) {
5914
5931
  const name = (options.name ?? "").trim();
@@ -5960,7 +5977,7 @@ async function resolveVaultWrite(options) {
5960
5977
  throw new Error(`refusing to store a secret for "${options.env}" without --yes`);
5961
5978
  }
5962
5979
  const value = await secretInputValue(options, "secret");
5963
- return { cfg, tenantId: (0, import_apps3.tenantIdFor)(cfg.app.id, options.env), value, doFetch, out };
5980
+ return { cfg, tenantId: (0, import_apps4.tenantIdFor)(cfg.app.id, options.env), value, doFetch, out };
5964
5981
  }
5965
5982
 
5966
5983
  // src/security-command-context.ts
@@ -6601,16 +6618,16 @@ async function githubSecurityCommand(parsed, dependencies) {
6601
6618
  }
6602
6619
  assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
6603
6620
  const context = await hostedSecurityContext(parsed, dependencies);
6604
- const repository = stringOpt(parsed.options.repo) ?? await inferGitHubRepository(process.cwd(), dependencies.readGitOrigin);
6621
+ const repository = stringOpt(parsed.options.repo) ?? await inferGitHubRepository(process.cwd(), dependencies.readGitOrigin).catch(() => void 0);
6605
6622
  const connection = await connectGitHubSecuritySource({
6606
6623
  ...context,
6607
- repository,
6624
+ ...repository === void 0 ? {} : { repository },
6608
6625
  open: parsed.options.open !== false,
6609
6626
  openInstallUrl: dependencies.openUrl ?? openUrl,
6610
6627
  wait: dependencies.pollWait,
6611
6628
  stdout: context.stdout
6612
6629
  });
6613
- context.stdout.log(`github: connected ${connection.repository ?? repository} (${connection.sourceId ?? "source pending"})`);
6630
+ context.stdout.log(`github: connected ${connection.repository ?? repository ?? "app repository"} (${connection.sourceId ?? "source pending"})`);
6614
6631
  context.stdout.log("github: odla.ai stores the installation; no PAT or GitHub token is written locally");
6615
6632
  }
6616
6633
  async function listSecuritySources(parsed, dependencies) {