@odla-ai/cli 0.28.2 → 0.30.0
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/README.md +59 -26
- package/REQUIREMENTS.md +4 -0
- package/dist/bin.cjs +333 -80
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-STJCWZJU.js → chunk-PPSXHZJI.js} +301 -81
- package/dist/chunk-PPSXHZJI.js.map +1 -0
- package/dist/{cli-2WZIFXA6.js → cli-CKBUVTJM.js} +2 -2
- package/dist/index.cjs +300 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -5
- package/dist/index.d.ts +11 -5
- package/dist/index.js +1 -1
- package/dist/runtime/pi-agent.js +18728 -15
- package/package.json +1 -1
- package/skills/odla/SKILL.md +12 -7
- package/skills/odla/references/build.md +8 -2
- package/skills/odla/references/co-owners.md +3 -2
- package/skills/odla/references/pm-work-intake.md +5 -3
- package/skills/odla/references/pm.md +4 -0
- package/skills/odla-migrate/references/secrets-map.md +22 -22
- package/skills/odla-migrate/references/troubleshooting.md +14 -16
- package/dist/chunk-STJCWZJU.js.map +0 -1
- /package/dist/{cli-2WZIFXA6.js.map → cli-CKBUVTJM.js.map} +0 -0
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-
|
|
177
|
+
requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-CKBUVTJM.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
|
});
|
|
@@ -1131,7 +1131,7 @@ var DEFAULT_ENVS = ["dev"];
|
|
|
1131
1131
|
var DEFAULT_SERVICES = ["db", "ai"];
|
|
1132
1132
|
var configImportSerial = 0;
|
|
1133
1133
|
var GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
|
|
1134
|
-
async function loadProjectConfig(configPath = "odla.config.mjs") {
|
|
1134
|
+
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1135
1135
|
const resolved = resolve2(configPath);
|
|
1136
1136
|
if (!existsSync3(resolved)) {
|
|
1137
1137
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
@@ -1144,7 +1144,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs") {
|
|
|
1144
1144
|
const envs = unique2(raw.envs?.length ? raw.envs : DEFAULT_ENVS);
|
|
1145
1145
|
const services = unique2(raw.services?.length ? raw.services : DEFAULT_SERVICES);
|
|
1146
1146
|
validateServices(services, resolved);
|
|
1147
|
-
validateCalendarConfig(raw, envs, services, resolved);
|
|
1147
|
+
validateCalendarConfig(raw, unique2([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1148
1148
|
const local = {
|
|
1149
1149
|
tokenFile: resolve2(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1150
1150
|
credentialsFile: resolve2(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
@@ -1194,12 +1194,12 @@ function buildPlan(cfg) {
|
|
|
1194
1194
|
}
|
|
1195
1195
|
function calendarServiceConfig(cfg, env) {
|
|
1196
1196
|
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
1197
|
-
if (!cfg.envs.includes(env)) throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1197
|
+
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1198
1198
|
const google = cfg.calendar?.google;
|
|
1199
1199
|
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
);
|
|
1200
|
+
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1201
|
+
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1202
|
+
const availability = unique2(configured.map((id) => id.trim()));
|
|
1203
1203
|
return {
|
|
1204
1204
|
provider: "google",
|
|
1205
1205
|
access: "book",
|
|
@@ -1266,13 +1266,18 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1266
1266
|
}
|
|
1267
1267
|
const availability = google[availabilityKey];
|
|
1268
1268
|
if (!isRecord6(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1269
|
-
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env));
|
|
1269
|
+
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1270
1270
|
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1271
1271
|
for (const env of envs) {
|
|
1272
1272
|
const ids = availability[env];
|
|
1273
1273
|
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1274
1274
|
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1275
1275
|
}
|
|
1276
|
+
}
|
|
1277
|
+
for (const [env, ids] of Object.entries(availability)) {
|
|
1278
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1279
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1280
|
+
}
|
|
1276
1281
|
if (ids.length > 10) {
|
|
1277
1282
|
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1278
1283
|
}
|
|
@@ -1282,7 +1287,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1282
1287
|
}
|
|
1283
1288
|
if (google.bookingCalendar !== void 0) {
|
|
1284
1289
|
if (!isRecord6(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1285
|
-
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env));
|
|
1290
|
+
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1286
1291
|
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1287
1292
|
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1288
1293
|
if (!safeText3(value2, 1024)) {
|
|
@@ -1292,7 +1297,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1292
1297
|
}
|
|
1293
1298
|
if (google.bookingPageUrl !== void 0) {
|
|
1294
1299
|
if (!isRecord6(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1295
|
-
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env));
|
|
1300
|
+
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1296
1301
|
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1297
1302
|
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1298
1303
|
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
@@ -1838,7 +1843,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
1838
1843
|
import { tenantIdFor } from "@odla-ai/apps";
|
|
1839
1844
|
function resolveEnv(cfg, requested) {
|
|
1840
1845
|
const env = requested ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
1841
|
-
if (!env || !cfg.envs.includes(env)) {
|
|
1846
|
+
if (!env || !cfg.envs.includes(env) && env !== "prod") {
|
|
1842
1847
|
throw new Error(`env "${env ?? ""}" is not declared in ${cfg.configPath}`);
|
|
1843
1848
|
}
|
|
1844
1849
|
return env;
|
|
@@ -1848,12 +1853,10 @@ function resolveTenant(cfg, requested) {
|
|
|
1848
1853
|
return { env, tenant: tenantIdFor(cfg.app.id, env) };
|
|
1849
1854
|
}
|
|
1850
1855
|
function bothTenants(cfg) {
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
);
|
|
1856
|
-
}
|
|
1856
|
+
if (!cfg.envs.includes("dev")) {
|
|
1857
|
+
throw new Error(
|
|
1858
|
+
`env "dev" is not declared in ${cfg.configPath} \u2014 sandbox/live transfers require an existing sandbox`
|
|
1859
|
+
);
|
|
1857
1860
|
}
|
|
1858
1861
|
return { sandbox: tenantIdFor(cfg.app.id, "dev"), live: tenantIdFor(cfg.app.id, "prod") };
|
|
1859
1862
|
}
|
|
@@ -1941,7 +1944,7 @@ function errorMessage(body) {
|
|
|
1941
1944
|
// src/human-session.ts
|
|
1942
1945
|
async function requireStudioHuman(configPath, action2, destination = "app", env) {
|
|
1943
1946
|
const cfg = await loadProjectConfig(configPath);
|
|
1944
|
-
const appEnv = env && cfg.envs.includes(env) ? env : cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
1947
|
+
const appEnv = env && (cfg.envs.includes(env) || env === "prod") ? env : cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
1945
1948
|
const path = destination === "app" ? `/studio/apps/${encodeURIComponent(cfg.app.id)}/${encodeURIComponent(appEnv)}/settings/app` : `/studio/apps/${encodeURIComponent(cfg.app.id)}/${encodeURIComponent(appEnv)}/${destination}`;
|
|
1946
1949
|
throw new Error(
|
|
1947
1950
|
`human_session_required: ${action2} requires the signed-in owner in Studio. A CLI device token is an agent credential, so another approval or retry cannot work. ${new URL(path, cfg.platformUrl).href}`
|
|
@@ -2586,8 +2589,7 @@ async function ensureCalendarConnected(ctx, options) {
|
|
|
2586
2589
|
async function lifecycleContext(options, optionalProjectCapabilities = []) {
|
|
2587
2590
|
const cfg = await loadProjectConfig(options.configPath);
|
|
2588
2591
|
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
2589
|
-
const env =
|
|
2590
|
-
if (!env || !cfg.envs.includes(env)) throw new Error(`env "${env ?? ""}" is not declared in ${options.configPath}`);
|
|
2592
|
+
const env = resolveEnv(cfg, options.env);
|
|
2591
2593
|
calendarServiceConfig(cfg, env);
|
|
2592
2594
|
const out = options.stdout ?? console;
|
|
2593
2595
|
const doFetch = options.fetch ?? fetch;
|
|
@@ -2788,8 +2790,7 @@ function printGroup(out, heading, items) {
|
|
|
2788
2790
|
import { DEFAULT_CATALOG } from "@odla-ai/ai";
|
|
2789
2791
|
async function aiModels(options = {}) {
|
|
2790
2792
|
const cfg = await loadProjectConfig(options.configPath ?? "odla.config.mjs");
|
|
2791
|
-
const env = options.env
|
|
2792
|
-
if (!cfg.envs.includes(env)) throw new Error(`ai models env "${env}" is not declared in config envs`);
|
|
2793
|
+
const env = resolveEnv(cfg, options.env);
|
|
2793
2794
|
const url = new URL(`/registry/apps/${encodeURIComponent(cfg.app.id)}/public-config`, cfg.platformUrl);
|
|
2794
2795
|
url.searchParams.set("env", env);
|
|
2795
2796
|
const response2 = await (options.fetch ?? fetch)(url);
|
|
@@ -3830,6 +3831,41 @@ async function wranglerLoggedIn(run, cwd) {
|
|
|
3830
3831
|
return false;
|
|
3831
3832
|
}
|
|
3832
3833
|
}
|
|
3834
|
+
async function wranglerRuntimeTarget(run, opts) {
|
|
3835
|
+
const configPath = findWranglerConfig(opts.cwd);
|
|
3836
|
+
if (!configPath) throw new Error(`no wrangler config found in ${opts.cwd}`);
|
|
3837
|
+
const config = readWranglerConfig(configPath);
|
|
3838
|
+
if (!config) throw new Error("runtime credential delivery requires wrangler.json or wrangler.jsonc");
|
|
3839
|
+
const baseName = typeof config.name === "string" ? config.name : "";
|
|
3840
|
+
const envs = config.env && typeof config.env === "object" && !Array.isArray(config.env) ? config.env : {};
|
|
3841
|
+
const envConfig = opts.env && envs[opts.env] && typeof envs[opts.env] === "object" ? envs[opts.env] : {};
|
|
3842
|
+
const explicitName = typeof envConfig.name === "string" ? envConfig.name : "";
|
|
3843
|
+
const serviceEnvironments = config.legacy_env === false;
|
|
3844
|
+
if (serviceEnvironments && explicitName) {
|
|
3845
|
+
throw new Error("env.<name>.name is not allowed when wrangler legacy_env is false");
|
|
3846
|
+
}
|
|
3847
|
+
const scriptName = serviceEnvironments ? baseName : explicitName || (opts.env ? `${baseName}-${opts.env}` : baseName);
|
|
3848
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,62}$/.test(scriptName)) {
|
|
3849
|
+
throw new Error("wrangler config must resolve an exact Worker name before credentials are issued");
|
|
3850
|
+
}
|
|
3851
|
+
const configuredAccount = typeof envConfig.account_id === "string" ? envConfig.account_id : typeof config.account_id === "string" ? config.account_id : "";
|
|
3852
|
+
const whoami = await run("npx", ["wrangler", "whoami"], { cwd: opts.cwd });
|
|
3853
|
+
if (whoami.code !== 0 || /not authenticated/i.test(`${whoami.stdout}${whoami.stderr}`)) {
|
|
3854
|
+
throw new Error(`wrangler is not logged in \u2014 run "wrangler login" (a browser step for the human)`);
|
|
3855
|
+
}
|
|
3856
|
+
const discovered = [...new Set(`${whoami.stdout}
|
|
3857
|
+
${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id) => id.toLowerCase()) ?? [])];
|
|
3858
|
+
const accountId = configuredAccount.toLowerCase() || (discovered.length === 1 ? discovered[0] : "");
|
|
3859
|
+
if (!/^[a-f0-9]{32}$/.test(accountId) || configuredAccount && discovered.length > 0 && !discovered.includes(accountId)) {
|
|
3860
|
+
throw new Error("Wrangler account is ambiguous or does not match account_id in the config");
|
|
3861
|
+
}
|
|
3862
|
+
return {
|
|
3863
|
+
provider: "cloudflare",
|
|
3864
|
+
accountId,
|
|
3865
|
+
scriptName,
|
|
3866
|
+
...opts.env ? { environment: opts.env } : {}
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3833
3869
|
function wranglerPutSecret(run, opts) {
|
|
3834
3870
|
const args = [
|
|
3835
3871
|
"wrangler",
|
|
@@ -3841,6 +3877,16 @@ function wranglerPutSecret(run, opts) {
|
|
|
3841
3877
|
];
|
|
3842
3878
|
return run("npx", args, { input: opts.value, cwd: opts.cwd });
|
|
3843
3879
|
}
|
|
3880
|
+
function wranglerBulkSecrets(run, opts) {
|
|
3881
|
+
const args = [
|
|
3882
|
+
"wrangler",
|
|
3883
|
+
"secret",
|
|
3884
|
+
"bulk",
|
|
3885
|
+
...opts.env ? ["--env", opts.env] : [],
|
|
3886
|
+
...opts.configPath ? ["--config", opts.configPath] : []
|
|
3887
|
+
];
|
|
3888
|
+
return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
|
|
3889
|
+
}
|
|
3844
3890
|
|
|
3845
3891
|
// src/doctor-checks.ts
|
|
3846
3892
|
function lintRules(rules, entities, publicRead) {
|
|
@@ -4329,16 +4375,10 @@ var PROD_ENV_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
|
4329
4375
|
async function secretsPush(options) {
|
|
4330
4376
|
await secretsPushImpl(options, true);
|
|
4331
4377
|
}
|
|
4332
|
-
async function secretsPushAfterPreflight(options) {
|
|
4333
|
-
await secretsPushImpl(options, false);
|
|
4334
|
-
}
|
|
4335
4378
|
async function secretsPushImpl(options, preflight) {
|
|
4336
4379
|
const out = options.stdout ?? console;
|
|
4337
4380
|
const cfg = await loadProjectConfig(options.configPath);
|
|
4338
|
-
const env = options.env;
|
|
4339
|
-
if (!cfg.envs.includes(env)) {
|
|
4340
|
-
throw new Error(`env "${env}" is not in config envs (${cfg.envs.join(", ")})`);
|
|
4341
|
-
}
|
|
4381
|
+
const env = resolveEnv(cfg, options.env);
|
|
4342
4382
|
if (PROD_ENV_NAMES.has(env) && !options.yes) {
|
|
4343
4383
|
throw new Error(`refusing to push a secret to "${env}" without --yes`);
|
|
4344
4384
|
}
|
|
@@ -4448,14 +4488,12 @@ async function resolveVaultWrite(options) {
|
|
|
4448
4488
|
const out = options.stdout ?? console;
|
|
4449
4489
|
const doFetch = options.fetch ?? fetch;
|
|
4450
4490
|
const cfg = await loadProjectConfig(options.configPath);
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
if (PROD_ENV_NAMES2.has(options.env) && !options.yes) {
|
|
4455
|
-
throw new Error(`refusing to store a secret for "${options.env}" without --yes`);
|
|
4491
|
+
const env = resolveEnv(cfg, options.env);
|
|
4492
|
+
if (PROD_ENV_NAMES2.has(env) && !options.yes) {
|
|
4493
|
+
throw new Error(`refusing to store a secret for "${env}" without --yes`);
|
|
4456
4494
|
}
|
|
4457
4495
|
const value2 = await secretInputValue(options, "secret");
|
|
4458
|
-
return { cfg, tenantId: tenantIdFor3(cfg.app.id,
|
|
4496
|
+
return { cfg, tenantId: tenantIdFor3(cfg.app.id, env), value: value2, doFetch, out };
|
|
4459
4497
|
}
|
|
4460
4498
|
|
|
4461
4499
|
// src/skill.ts
|
|
@@ -4742,8 +4780,7 @@ function listFiles(dir) {
|
|
|
4742
4780
|
async function smoke(options) {
|
|
4743
4781
|
const out = options.stdout ?? console;
|
|
4744
4782
|
const cfg = await loadProjectConfig(options.configPath);
|
|
4745
|
-
const env =
|
|
4746
|
-
if (!cfg.envs.includes(env)) throw new Error(`env "${env}" is not declared in ${displayPath(cfg.configPath, cfg.rootDir)}`);
|
|
4783
|
+
const env = resolveEnv(cfg, options.env);
|
|
4747
4784
|
const credentials = readCredentials(cfg.local.credentialsFile);
|
|
4748
4785
|
if (!credentials) {
|
|
4749
4786
|
throw new Error(`local credentials missing: ${displayPath(cfg.local.credentialsFile, cfg.rootDir)}. Run "odla-ai provision --write-dev-vars".`);
|
|
@@ -8702,6 +8739,55 @@ function requireName(parsed) {
|
|
|
8702
8739
|
return name;
|
|
8703
8740
|
}
|
|
8704
8741
|
|
|
8742
|
+
// src/credential-command.ts
|
|
8743
|
+
async function responseError(response2) {
|
|
8744
|
+
return redactSecrets((await response2.text()).slice(0, 1e3));
|
|
8745
|
+
}
|
|
8746
|
+
async function credentialCommand(parsed, deps = {}) {
|
|
8747
|
+
const action2 = parsed.positionals[1] ?? "list";
|
|
8748
|
+
if (action2 !== "list" && action2 !== "revoke") {
|
|
8749
|
+
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
8750
|
+
}
|
|
8751
|
+
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
8752
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
8753
|
+
const doFetch = deps.fetch ?? fetch;
|
|
8754
|
+
const out = deps.stdout ?? console;
|
|
8755
|
+
const token = await getDeveloperToken(cfg, {
|
|
8756
|
+
configPath: cfg.configPath,
|
|
8757
|
+
token: stringOpt(parsed.options.token),
|
|
8758
|
+
email: stringOpt(parsed.options.email),
|
|
8759
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
8760
|
+
openApprovalUrl: deps.openUrl
|
|
8761
|
+
}, doFetch, out, { optionalProjectCapabilities: ["app.manage"] });
|
|
8762
|
+
const base = `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials`;
|
|
8763
|
+
if (action2 === "revoke") {
|
|
8764
|
+
const id = parsed.positionals[2];
|
|
8765
|
+
if (!id) throw new Error("credentials revoke requires the exact receipt id from credentials list");
|
|
8766
|
+
const response3 = await doFetch(`${base}/${encodeURIComponent(id)}`, {
|
|
8767
|
+
method: "DELETE",
|
|
8768
|
+
headers: { authorization: `Bearer ${token}` }
|
|
8769
|
+
});
|
|
8770
|
+
if (!response3.ok) throw new Error(`runtime credential revoke failed (${response3.status}): ${await responseError(response3)}`);
|
|
8771
|
+
const body2 = await response3.json();
|
|
8772
|
+
return out.log(parsed.options.json === true ? JSON.stringify(body2, null, 2) : `revoked ${body2.receipt.id} (${body2.receipt.env} ${body2.receipt.target.scriptName})`);
|
|
8773
|
+
}
|
|
8774
|
+
const query = new URLSearchParams();
|
|
8775
|
+
const requestedEnv = stringOpt(parsed.options.env);
|
|
8776
|
+
if (requestedEnv) query.set("env", requestedEnv);
|
|
8777
|
+
const response2 = await doFetch(`${base}${query.size ? `?${query}` : ""}`, {
|
|
8778
|
+
headers: { authorization: `Bearer ${token}` }
|
|
8779
|
+
});
|
|
8780
|
+
if (!response2.ok) throw new Error(`runtime credential inventory failed (${response2.status}): ${await responseError(response2)}`);
|
|
8781
|
+
const body = await response2.json();
|
|
8782
|
+
const credentials = parsed.options.all === true ? body.credentials : body.credentials.filter((item) => item.state === "committed");
|
|
8783
|
+
if (parsed.options.json === true) return out.log(JSON.stringify({ credentials }, null, 2));
|
|
8784
|
+
if (!credentials.length) return out.log("no active runtime credential receipts");
|
|
8785
|
+
out.log("RECEIPT ENV TARGET CREATED");
|
|
8786
|
+
for (const item of credentials) {
|
|
8787
|
+
out.log(`${item.id} ${item.env} ${item.target.accountId ?? "local"}/${item.target.scriptName} ${new Date(item.createdAt).toISOString()}`);
|
|
8788
|
+
}
|
|
8789
|
+
}
|
|
8790
|
+
|
|
8705
8791
|
// src/help-usage.ts
|
|
8706
8792
|
var USAGE_SECTION = `
|
|
8707
8793
|
Start here:
|
|
@@ -8819,7 +8905,9 @@ Usage:
|
|
|
8819
8905
|
odla-ai security report <job-id> [--json]
|
|
8820
8906
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
8821
8907
|
odla-ai security run [target] --self --ack-redacted-source
|
|
8822
|
-
odla-ai provision [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
|
|
8908
|
+
odla-ai provision [--live] [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
|
|
8909
|
+
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
8910
|
+
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
8823
8911
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--email <odla-account>] [--no-open]
|
|
8824
8912
|
odla-ai skill install [--dir <project>] [--agent <name>] [--global] [--force]
|
|
8825
8913
|
odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
|
|
@@ -8919,6 +9007,9 @@ Commands:
|
|
|
8919
9007
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
8920
9008
|
explicit unknowns, and next actions through a read-only grant.
|
|
8921
9009
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
9010
|
+
"provision --live --yes" initializes only the live instance of
|
|
9011
|
+
an existing sandbox app and enables every configured service;
|
|
9012
|
+
no edit to the dev-first envs list is required.
|
|
8922
9013
|
smoke Verify credentials, public-config, composed schema, db aggregate, and integration probes.
|
|
8923
9014
|
skill Same installer; --agent accepts all, claude, codex, cursor,
|
|
8924
9015
|
copilot, gemini, or agents (repeatable or comma-separated).
|
|
@@ -8931,15 +9022,21 @@ Safety:
|
|
|
8931
9022
|
Every app has two databases on odla.ai: a sandbox (env "dev", tenant
|
|
8932
9023
|
<appId>--dev) and a live one (env "prod", tenant <appId>). Both are served by
|
|
8933
9024
|
production odla.ai \u2014 there is no separate odla to point at. New projects get
|
|
8934
|
-
the sandbox only.
|
|
8935
|
-
|
|
9025
|
+
the sandbox only. Run "provision --live --dry-run", then "provision --live
|
|
9026
|
+
--yes" to initialize live from that existing sandbox project's config. Add
|
|
9027
|
+
"prod" to envs only when ordinary provision runs should manage both; use
|
|
9028
|
+
--dry-run first to inspect either resolved plan.
|
|
8936
9029
|
A real provision first verifies both the released CLI version and the exact
|
|
8937
9030
|
pinned versions of every external @odla-ai runtime module before importing
|
|
8938
9031
|
the command graph. A stale workspace module blocks with its resolved path and
|
|
8939
9032
|
tells the agent to update/rebase, npm ci, and rebuild.
|
|
8940
|
-
Provision caches the approved developer token
|
|
8941
|
-
|
|
8942
|
-
|
|
9033
|
+
Provision caches the approved developer token under .odla/ with mode 0600.
|
|
9034
|
+
Local-only development provisioning may also cache that developer's service
|
|
9035
|
+
credentials there. \`provision --push-secrets\` instead stages a fresh,
|
|
9036
|
+
independently revocable credential set through the approved handshake,
|
|
9037
|
+
transfers the complete set to the exact Worker with \`wrangler secret bulk\`
|
|
9038
|
+
over stdin, and never writes its plaintext under .odla. Secret push preflights
|
|
9039
|
+
Wrangler before shown-once issuance; it never rotates sibling credentials.
|
|
8943
9040
|
Projectless PM, Discussions, o11y, runbook, and identity commands use
|
|
8944
9041
|
--platform/--app/--env, ODLA_PLATFORM_URL/ODLA_APP_ID/ODLA_ENV, and
|
|
8945
9042
|
ODLA_DEV_TOKEN. Save non-secret scope metadata with "context save", then
|
|
@@ -10863,7 +10960,7 @@ async function issueO11yToken(opts) {
|
|
|
10863
10960
|
);
|
|
10864
10961
|
if (res.status === 409 && !opts.rotateO11y) {
|
|
10865
10962
|
throw new Error(
|
|
10866
|
-
`o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --
|
|
10963
|
+
`o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --push-secrets" to install a separate runtime credential without rotating siblings`
|
|
10867
10964
|
);
|
|
10868
10965
|
}
|
|
10869
10966
|
if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText7(res)}`);
|
|
@@ -10879,23 +10976,131 @@ async function safeText7(res) {
|
|
|
10879
10976
|
}
|
|
10880
10977
|
}
|
|
10881
10978
|
|
|
10979
|
+
// src/runtime-credentials.ts
|
|
10980
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
10981
|
+
function runtimeUrl(cfg, suffix = "") {
|
|
10982
|
+
return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
|
|
10983
|
+
}
|
|
10984
|
+
async function safeError(response2) {
|
|
10985
|
+
const text2 = await response2.text();
|
|
10986
|
+
return redactSecrets(text2.slice(0, 1e3));
|
|
10987
|
+
}
|
|
10988
|
+
async function finish(doFetch, cfg, token, sessionId, method) {
|
|
10989
|
+
return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
|
|
10990
|
+
method,
|
|
10991
|
+
headers: { authorization: `Bearer ${token}` }
|
|
10992
|
+
});
|
|
10993
|
+
}
|
|
10994
|
+
async function deliverRuntimeCredentials(cfg, options) {
|
|
10995
|
+
const doFetch = options.fetch ?? fetch;
|
|
10996
|
+
const run = options.runner ?? defaultRunner;
|
|
10997
|
+
const wranglerEnv = options.env === "prod" || options.env === "production" ? void 0 : options.env;
|
|
10998
|
+
const target = await wranglerRuntimeTarget(run, {
|
|
10999
|
+
cwd: cfg.rootDir,
|
|
11000
|
+
env: wranglerEnv
|
|
11001
|
+
});
|
|
11002
|
+
const issue = await doFetch(runtimeUrl(cfg), {
|
|
11003
|
+
method: "POST",
|
|
11004
|
+
headers: {
|
|
11005
|
+
authorization: `Bearer ${options.developerToken}`,
|
|
11006
|
+
"content-type": "application/json"
|
|
11007
|
+
},
|
|
11008
|
+
body: JSON.stringify({
|
|
11009
|
+
env: options.env,
|
|
11010
|
+
idempotencyKey: `wrangler:${randomUUID3()}`,
|
|
11011
|
+
target
|
|
11012
|
+
})
|
|
11013
|
+
});
|
|
11014
|
+
const body = await issue.json().catch(() => null);
|
|
11015
|
+
if (!issue.ok || typeof body?.sessionId !== "string" || !body.secrets || typeof body.secrets !== "object") {
|
|
11016
|
+
const detail = body?.error?.code ?? body?.error?.message ?? issue.status;
|
|
11017
|
+
throw new Error(`runtime credential staging failed: ${String(detail)}`);
|
|
11018
|
+
}
|
|
11019
|
+
const secrets = body.secrets;
|
|
11020
|
+
const values = {};
|
|
11021
|
+
for (const name of ["ODLA_API_KEY", "ODLA_O11Y_TOKEN"]) {
|
|
11022
|
+
const value2 = secrets[name];
|
|
11023
|
+
if (typeof value2 === "string" && value2) values[name] = value2;
|
|
11024
|
+
}
|
|
11025
|
+
if (Object.keys(values).length === 0) {
|
|
11026
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11027
|
+
throw new Error("runtime credential staging returned no configured service credentials");
|
|
11028
|
+
}
|
|
11029
|
+
const pushed = await wranglerBulkSecrets(run, {
|
|
11030
|
+
secrets: values,
|
|
11031
|
+
env: wranglerEnv,
|
|
11032
|
+
cwd: cfg.rootDir
|
|
11033
|
+
});
|
|
11034
|
+
if (pushed.code !== 0) {
|
|
11035
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11036
|
+
const detail = redactSecrets(`${pushed.stderr || pushed.stdout}`.trim());
|
|
11037
|
+
throw new Error(`wrangler secret bulk failed (exit ${pushed.code}): ${detail}`);
|
|
11038
|
+
}
|
|
11039
|
+
const committed = await finish(
|
|
11040
|
+
doFetch,
|
|
11041
|
+
cfg,
|
|
11042
|
+
options.developerToken,
|
|
11043
|
+
body.sessionId,
|
|
11044
|
+
"POST"
|
|
11045
|
+
);
|
|
11046
|
+
if (!committed.ok) {
|
|
11047
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11048
|
+
throw new Error(`runtime credential receipt commit failed: ${await safeError(committed)}`);
|
|
11049
|
+
}
|
|
11050
|
+
options.stdout?.log(
|
|
11051
|
+
`${options.env}: installed ${Object.keys(values).join(" + ")} on ${target.accountId}/${target.scriptName}${target.environment ? ` (${target.environment})` : ""}; plaintext stayed in memory`
|
|
11052
|
+
);
|
|
11053
|
+
return {
|
|
11054
|
+
sessionId: body.sessionId,
|
|
11055
|
+
target,
|
|
11056
|
+
...values.ODLA_API_KEY ? { dbKey: values.ODLA_API_KEY } : {},
|
|
11057
|
+
...values.ODLA_O11Y_TOKEN ? { o11yToken: values.ODLA_O11Y_TOKEN } : {}
|
|
11058
|
+
};
|
|
11059
|
+
}
|
|
11060
|
+
|
|
11061
|
+
// src/provision-live.ts
|
|
11062
|
+
function liveProvisionConfig(cfg) {
|
|
11063
|
+
if (!cfg.envs.includes("dev")) {
|
|
11064
|
+
throw new Error(
|
|
11065
|
+
`--live initializes an existing sandbox, but env "dev" is not declared in ${cfg.configPath}; use normal "odla-ai provision --yes" for a prod-only project`
|
|
11066
|
+
);
|
|
11067
|
+
}
|
|
11068
|
+
if (cfg.services.includes("calendar")) {
|
|
11069
|
+
const google = cfg.calendar?.google;
|
|
11070
|
+
const availability = google?.availabilityCalendars ?? google?.calendars;
|
|
11071
|
+
if (!availability?.prod?.length) {
|
|
11072
|
+
throw new Error(
|
|
11073
|
+
`--live requires calendar.google.availabilityCalendars.prod in ${cfg.configPath}; live uses a separate Google Calendar connection, so sandbox calendar ids are not copied`
|
|
11074
|
+
);
|
|
11075
|
+
}
|
|
11076
|
+
}
|
|
11077
|
+
return { ...cfg, envs: ["prod"] };
|
|
11078
|
+
}
|
|
11079
|
+
|
|
10882
11080
|
// src/provision.ts
|
|
10883
11081
|
async function provision(options) {
|
|
10884
11082
|
const out = options.stdout ?? console;
|
|
10885
|
-
const
|
|
11083
|
+
const loaded = await loadProjectConfig(
|
|
11084
|
+
options.configPath,
|
|
11085
|
+
options.live ? { additionalEnvs: ["prod"] } : void 0
|
|
11086
|
+
);
|
|
11087
|
+
const cfg = options.live ? liveProvisionConfig(loaded) : loaded;
|
|
10886
11088
|
const plan = buildPlan(cfg);
|
|
10887
11089
|
const hasDb = cfg.services.includes("db");
|
|
10888
11090
|
const hasO11y = cfg.services.includes("o11y");
|
|
10889
11091
|
if (options.rotateO11yToken && !hasO11y) {
|
|
10890
11092
|
throw new Error("--rotate-o11y-token requires the o11y service in odla.config.mjs");
|
|
10891
11093
|
}
|
|
10892
|
-
if (options.pushSecrets && options.
|
|
10893
|
-
throw new Error(
|
|
11094
|
+
if (options.pushSecrets && (options.rotateKeys || options.rotateO11yToken)) {
|
|
11095
|
+
throw new Error(
|
|
11096
|
+
"--push-secrets always issues an independent runtime credential set; do not combine it with destructive rotation flags"
|
|
11097
|
+
);
|
|
10894
11098
|
}
|
|
10895
11099
|
out.log(`odla-ai: ${plan.appName} (${plan.appId})`);
|
|
10896
11100
|
out.log(` platform: ${plan.platformUrl}`);
|
|
10897
11101
|
out.log(` db: ${plan.dbEndpoint}`);
|
|
10898
11102
|
out.log(` envs: ${plan.envs.join(", ")}`);
|
|
11103
|
+
if (options.live) out.log(" target: live setup (existing sandbox app)");
|
|
10899
11104
|
out.log(` services: ${plan.services.join(", ")}`);
|
|
10900
11105
|
out.log(` integrations: ${plan.integrations.length ? plan.integrations.join(", ") : "none"}`);
|
|
10901
11106
|
const productionEnvs = plan.envs.filter((env) => env === "prod" || env === "production");
|
|
@@ -10926,7 +11131,7 @@ async function provision(options) {
|
|
|
10926
11131
|
out.log(` secrets: ${options.pushSecrets ? "push configured Worker secrets" : "local only"}`);
|
|
10927
11132
|
return;
|
|
10928
11133
|
}
|
|
10929
|
-
let credentials = readCredentials(cfg.local.credentialsFile);
|
|
11134
|
+
let credentials = options.pushSecrets ? null : readCredentials(cfg.local.credentialsFile);
|
|
10930
11135
|
if (credentials && credentials.appId !== cfg.app.id) {
|
|
10931
11136
|
throw new Error(
|
|
10932
11137
|
`credentials at ${displayPath(cfg.local.credentialsFile, cfg.rootDir)} are for "${credentials.appId}", not "${cfg.app.id}"`
|
|
@@ -10935,7 +11140,7 @@ async function provision(options) {
|
|
|
10935
11140
|
const rotatesO11y = !!(options.rotateKeys || options.rotateO11yToken);
|
|
10936
11141
|
const missingO11y = cfg.envs.some((env) => !credentials?.envs[env]?.o11yToken);
|
|
10937
11142
|
const missingDb = cfg.envs.some((env) => !credentials?.envs[env]?.dbKey);
|
|
10938
|
-
const losesShownOnceCredential = hasDb && (!!options.rotateKeys || missingDb) || hasO11y && (rotatesO11y || missingO11y);
|
|
11143
|
+
const losesShownOnceCredential = !options.pushSecrets && (hasDb && (!!options.rotateKeys || missingDb) || hasO11y && (rotatesO11y || missingO11y));
|
|
10939
11144
|
if (options.writeCredentials === false && losesShownOnceCredential) {
|
|
10940
11145
|
throw new Error("credential issuance/rotation requires the private credentials file; remove --no-write-credentials");
|
|
10941
11146
|
}
|
|
@@ -10956,6 +11161,10 @@ async function provision(options) {
|
|
|
10956
11161
|
const existing = await apps.resolveApp(cfg.app.id);
|
|
10957
11162
|
if (existing) {
|
|
10958
11163
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
11164
|
+
} else if (options.live) {
|
|
11165
|
+
throw new Error(
|
|
11166
|
+
`cannot set up live for "${cfg.app.id}": the sandbox app does not exist yet; run "odla-ai provision" first`
|
|
11167
|
+
);
|
|
10959
11168
|
} else {
|
|
10960
11169
|
try {
|
|
10961
11170
|
await apps.createApp({ name: cfg.app.name, appId: cfg.app.id });
|
|
@@ -11005,38 +11214,42 @@ async function provision(options) {
|
|
|
11005
11214
|
await applyCalendarBookingPage(calendarCtx, calendarBookingPageUrl(cfg, env), out);
|
|
11006
11215
|
}
|
|
11007
11216
|
}
|
|
11217
|
+
let devVarsCredentials = credentials;
|
|
11008
11218
|
for (const env of cfg.envs) {
|
|
11009
11219
|
const tenantId = tenantIdFor5(cfg.app.id, env);
|
|
11010
|
-
|
|
11011
|
-
cfg,
|
|
11012
|
-
env,
|
|
11013
|
-
developerToken: token,
|
|
11014
|
-
credentials,
|
|
11015
|
-
rotateDb: !!options.rotateKeys,
|
|
11016
|
-
rotateO11y: rotatesO11y,
|
|
11017
|
-
write: options.writeCredentials !== false,
|
|
11018
|
-
fetch: doFetch,
|
|
11019
|
-
stdout: out
|
|
11020
|
-
});
|
|
11021
|
-
const dbKey = credentials.envs[env]?.dbKey;
|
|
11220
|
+
let dbKey;
|
|
11022
11221
|
if (options.pushSecrets) {
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
|
|
11036
|
-
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
}
|
|
11222
|
+
const delivered = await deliverRuntimeCredentials(cfg, {
|
|
11223
|
+
env,
|
|
11224
|
+
developerToken: token,
|
|
11225
|
+
fetch: doFetch,
|
|
11226
|
+
runner: options.secretRunner,
|
|
11227
|
+
stdout: out
|
|
11228
|
+
});
|
|
11229
|
+
dbKey = delivered.dbKey;
|
|
11230
|
+
devVarsCredentials = mergeCredential(devVarsCredentials, {
|
|
11231
|
+
appId: cfg.app.id,
|
|
11232
|
+
platformUrl: cfg.platformUrl,
|
|
11233
|
+
dbEndpoint: cfg.dbEndpoint,
|
|
11234
|
+
env,
|
|
11235
|
+
tenantId,
|
|
11236
|
+
...delivered.dbKey ? { dbKey: delivered.dbKey } : {},
|
|
11237
|
+
...delivered.o11yToken ? { o11yToken: delivered.o11yToken } : {}
|
|
11238
|
+
});
|
|
11239
|
+
} else {
|
|
11240
|
+
credentials = await provisionEnvCredentials({
|
|
11241
|
+
cfg,
|
|
11242
|
+
env,
|
|
11243
|
+
developerToken: token,
|
|
11244
|
+
credentials,
|
|
11245
|
+
rotateDb: !!options.rotateKeys,
|
|
11246
|
+
rotateO11y: rotatesO11y,
|
|
11247
|
+
write: options.writeCredentials !== false,
|
|
11248
|
+
fetch: doFetch,
|
|
11249
|
+
stdout: out
|
|
11250
|
+
});
|
|
11251
|
+
devVarsCredentials = credentials;
|
|
11252
|
+
dbKey = credentials.envs[env]?.dbKey;
|
|
11040
11253
|
}
|
|
11041
11254
|
if (schema && dbKey) {
|
|
11042
11255
|
await postJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(tenantId)}/schema`, dbKey, { schema });
|
|
@@ -11060,13 +11273,13 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11060
11273
|
}
|
|
11061
11274
|
}
|
|
11062
11275
|
}
|
|
11063
|
-
if (options.writeCredentials !== false && credentials) {
|
|
11276
|
+
if (!options.pushSecrets && options.writeCredentials !== false && credentials) {
|
|
11064
11277
|
const ignored = cfg.local.gitignore && gitignoreEntry(cfg.rootDir, cfg.local.credentialsFile);
|
|
11065
11278
|
out.log(`credentials: wrote ${displayPath(cfg.local.credentialsFile, cfg.rootDir)} (0600${ignored ? ", gitignored" : ""})`);
|
|
11066
11279
|
}
|
|
11067
|
-
if (devVarsTarget &&
|
|
11280
|
+
if (devVarsTarget && devVarsCredentials) {
|
|
11068
11281
|
const env = cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
11069
|
-
writeDevVars(devVarsTarget,
|
|
11282
|
+
writeDevVars(devVarsTarget, devVarsCredentials, env, o11yDevVars(cfg));
|
|
11070
11283
|
out.log(`dev vars: wrote ${displayPath(devVarsTarget, cfg.rootDir)} for ${env}`);
|
|
11071
11284
|
}
|
|
11072
11285
|
if (cfg.services.includes("calendar")) {
|
|
@@ -11157,6 +11370,7 @@ var COMMAND_SURFACE = {
|
|
|
11157
11370
|
code: { connect: {} },
|
|
11158
11371
|
config: { diff: {}, plan: {}, apply: {} },
|
|
11159
11372
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
11373
|
+
credentials: { list: {}, revoke: {} },
|
|
11160
11374
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
11161
11375
|
discuss: {
|
|
11162
11376
|
groups: {},
|
|
@@ -12841,6 +13055,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12841
13055
|
await contextCommand(parsed, runtime);
|
|
12842
13056
|
return;
|
|
12843
13057
|
}
|
|
13058
|
+
if (command === "credentials") {
|
|
13059
|
+
await credentialCommand(parsed, runtime);
|
|
13060
|
+
return;
|
|
13061
|
+
}
|
|
12844
13062
|
if (command === "runbook") {
|
|
12845
13063
|
await runbookCommand(parsed, runtime);
|
|
12846
13064
|
return;
|
|
@@ -12908,6 +13126,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12908
13126
|
async function provisionCommand(parsed, dependencies) {
|
|
12909
13127
|
assertArgs(parsed, [
|
|
12910
13128
|
"config",
|
|
13129
|
+
"live",
|
|
12911
13130
|
"dry-run",
|
|
12912
13131
|
"rotate-keys",
|
|
12913
13132
|
"rotate-o11y-token",
|
|
@@ -12924,6 +13143,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
12924
13143
|
const writeDevVars2 = parsed.options["write-dev-vars"];
|
|
12925
13144
|
const options = {
|
|
12926
13145
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
13146
|
+
live: parsed.options.live === true,
|
|
12927
13147
|
dryRun: parsed.options["dry-run"] === true,
|
|
12928
13148
|
rotateKeys: parsed.options["rotate-keys"] === true,
|
|
12929
13149
|
rotateO11yToken: parsed.options["rotate-o11y-token"] === true,
|
|
@@ -13026,4 +13246,4 @@ export {
|
|
|
13026
13246
|
isTerminalHostedSecurityStatus,
|
|
13027
13247
|
runCli
|
|
13028
13248
|
};
|
|
13029
|
-
//# sourceMappingURL=chunk-
|
|
13249
|
+
//# sourceMappingURL=chunk-PPSXHZJI.js.map
|