@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/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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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) {
|