@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 +62 -45
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-3AC74CD2.js → chunk-AN6KZMR5.js} +62 -45
- package/dist/chunk-AN6KZMR5.js.map +1 -0
- package/dist/index.cjs +62 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/references/co-owners.md +7 -0
- package/dist/chunk-3AC74CD2.js.map +0 -1
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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) {
|