@odla-ai/cli 0.28.2 → 0.29.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 +17 -3
- package/REQUIREMENTS.md +4 -0
- package/dist/bin.cjs +79 -37
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-STJCWZJU.js → chunk-YTVLTADT.js} +68 -38
- package/dist/chunk-YTVLTADT.js.map +1 -0
- package/dist/{cli-2WZIFXA6.js → cli-DNOAYDBR.js} +2 -2
- package/dist/index.cjs +67 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +8 -6
- package/skills/odla/references/build.md +8 -2
- package/skills/odla/references/co-owners.md +3 -2
- package/dist/chunk-STJCWZJU.js.map +0 -1
- /package/dist/{cli-2WZIFXA6.js.map → cli-DNOAYDBR.js.map} +0 -0
package/README.md
CHANGED
|
@@ -461,9 +461,23 @@ the previous bearer.
|
|
|
461
461
|
- `src/odla/rules.mjs`
|
|
462
462
|
- `.gitignore` entries for local credentials
|
|
463
463
|
|
|
464
|
-
New configs contain only the `dev` environment.
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
New configs contain only the `dev` environment. After the development flow is
|
|
465
|
+
healthy, initialize the existing app's live instance without editing the config:
|
|
466
|
+
|
|
467
|
+
```sh
|
|
468
|
+
npx @odla-ai/cli provision --live --dry-run
|
|
469
|
+
npx @odla-ai/cli provision --live --yes --push-secrets
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`--live` targets only `prod`, enables every configured service, provisions its
|
|
473
|
+
credentials/schema/integrations, and refuses to create an app when the sandbox
|
|
474
|
+
does not exist yet. Add `prod` to `envs` only when normal provision runs should
|
|
475
|
+
manage both environments. Calendar apps may predeclare
|
|
476
|
+
`calendar.google.availabilityCalendars.prod` while `envs` remains dev-only;
|
|
477
|
+
live still uses its own Google consent. Every production mutation requires
|
|
478
|
+
`--yes`. Afterwards, explicit live operations such as `smoke --env prod`,
|
|
479
|
+
`secrets push --env prod --yes`, and `app go-live` work while unqualified
|
|
480
|
+
commands continue to default to the sandbox.
|
|
467
481
|
|
|
468
482
|
`provision` performs the standard safe setup flow:
|
|
469
483
|
|
package/REQUIREMENTS.md
CHANGED
|
@@ -28,6 +28,10 @@ Agnacl, but none should mention or special-case Agnacl.
|
|
|
28
28
|
- Source semantics remain with the coding agent: package installation, Worker
|
|
29
29
|
instrumentation, and application-specific signals/rules. Humans approve
|
|
30
30
|
identity, production mutation, and explicit destructive rotation.
|
|
31
|
+
- An existing dev-first sandbox app must be able to initialize only its live
|
|
32
|
+
environment with one explicit CLI mode. That mode enables the complete
|
|
33
|
+
configured service set, requires production consent, refuses to create a new
|
|
34
|
+
app, and must not make live part of later unqualified provision runs.
|
|
31
35
|
|
|
32
36
|
## Human Approval
|
|
33
37
|
|
package/dist/bin.cjs
CHANGED
|
@@ -1410,7 +1410,7 @@ var init_integration_validation = __esm({
|
|
|
1410
1410
|
});
|
|
1411
1411
|
|
|
1412
1412
|
// src/config.ts
|
|
1413
|
-
async function loadProjectConfig(configPath = "odla.config.mjs") {
|
|
1413
|
+
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1414
1414
|
const resolved = (0, import_node_path5.resolve)(configPath);
|
|
1415
1415
|
if (!(0, import_node_fs7.existsSync)(resolved)) {
|
|
1416
1416
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
@@ -1423,7 +1423,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs") {
|
|
|
1423
1423
|
const envs = unique2(raw.envs?.length ? raw.envs : DEFAULT_ENVS);
|
|
1424
1424
|
const services = unique2(raw.services?.length ? raw.services : DEFAULT_SERVICES);
|
|
1425
1425
|
validateServices(services, resolved);
|
|
1426
|
-
validateCalendarConfig(raw, envs, services, resolved);
|
|
1426
|
+
validateCalendarConfig(raw, unique2([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1427
1427
|
const local = {
|
|
1428
1428
|
tokenFile: (0, import_node_path5.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1429
1429
|
credentialsFile: (0, import_node_path5.resolve)(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
@@ -1473,12 +1473,12 @@ function buildPlan(cfg) {
|
|
|
1473
1473
|
}
|
|
1474
1474
|
function calendarServiceConfig(cfg, env) {
|
|
1475
1475
|
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
1476
|
-
if (!cfg.envs.includes(env)) throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1476
|
+
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1477
1477
|
const google = cfg.calendar?.google;
|
|
1478
1478
|
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
);
|
|
1479
|
+
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1480
|
+
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1481
|
+
const availability = unique2(configured.map((id) => id.trim()));
|
|
1482
1482
|
return {
|
|
1483
1483
|
provider: "google",
|
|
1484
1484
|
access: "book",
|
|
@@ -1545,13 +1545,18 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1545
1545
|
}
|
|
1546
1546
|
const availability = google[availabilityKey];
|
|
1547
1547
|
if (!isRecord6(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1548
|
-
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env));
|
|
1548
|
+
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1549
1549
|
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1550
1550
|
for (const env of envs) {
|
|
1551
1551
|
const ids = availability[env];
|
|
1552
1552
|
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1553
1553
|
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1554
1554
|
}
|
|
1555
|
+
}
|
|
1556
|
+
for (const [env, ids] of Object.entries(availability)) {
|
|
1557
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1558
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1559
|
+
}
|
|
1555
1560
|
if (ids.length > 10) {
|
|
1556
1561
|
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1557
1562
|
}
|
|
@@ -1561,7 +1566,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1561
1566
|
}
|
|
1562
1567
|
if (google.bookingCalendar !== void 0) {
|
|
1563
1568
|
if (!isRecord6(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1564
|
-
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env));
|
|
1569
|
+
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1565
1570
|
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1566
1571
|
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1567
1572
|
if (!safeText3(value2, 1024)) {
|
|
@@ -1571,7 +1576,7 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1571
1576
|
}
|
|
1572
1577
|
if (google.bookingPageUrl !== void 0) {
|
|
1573
1578
|
if (!isRecord6(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1574
|
-
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env));
|
|
1579
|
+
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1575
1580
|
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1576
1581
|
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1577
1582
|
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
@@ -2187,7 +2192,7 @@ var init_auth_command = __esm({
|
|
|
2187
2192
|
// src/tenant.ts
|
|
2188
2193
|
function resolveEnv(cfg, requested) {
|
|
2189
2194
|
const env = requested ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
2190
|
-
if (!env || !cfg.envs.includes(env)) {
|
|
2195
|
+
if (!env || !cfg.envs.includes(env) && env !== "prod") {
|
|
2191
2196
|
throw new Error(`env "${env ?? ""}" is not declared in ${cfg.configPath}`);
|
|
2192
2197
|
}
|
|
2193
2198
|
return env;
|
|
@@ -2197,12 +2202,10 @@ function resolveTenant(cfg, requested) {
|
|
|
2197
2202
|
return { env, tenant: (0, import_apps2.tenantIdFor)(cfg.app.id, env) };
|
|
2198
2203
|
}
|
|
2199
2204
|
function bothTenants(cfg) {
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
);
|
|
2205
|
-
}
|
|
2205
|
+
if (!cfg.envs.includes("dev")) {
|
|
2206
|
+
throw new Error(
|
|
2207
|
+
`env "dev" is not declared in ${cfg.configPath} \u2014 sandbox/live transfers require an existing sandbox`
|
|
2208
|
+
);
|
|
2206
2209
|
}
|
|
2207
2210
|
return { sandbox: (0, import_apps2.tenantIdFor)(cfg.app.id, "dev"), live: (0, import_apps2.tenantIdFor)(cfg.app.id, "prod") };
|
|
2208
2211
|
}
|
|
@@ -2308,7 +2311,7 @@ var init_agent_command = __esm({
|
|
|
2308
2311
|
// src/human-session.ts
|
|
2309
2312
|
async function requireStudioHuman(configPath, action2, destination = "app", env) {
|
|
2310
2313
|
const cfg = await loadProjectConfig(configPath);
|
|
2311
|
-
const appEnv = env && cfg.envs.includes(env) ? env : cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
2314
|
+
const appEnv = env && (cfg.envs.includes(env) || env === "prod") ? env : cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
2312
2315
|
const path = destination === "app" ? `/studio/apps/${encodeURIComponent(cfg.app.id)}/${encodeURIComponent(appEnv)}/settings/app` : `/studio/apps/${encodeURIComponent(cfg.app.id)}/${encodeURIComponent(appEnv)}/${destination}`;
|
|
2313
2316
|
throw new Error(
|
|
2314
2317
|
`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}`
|
|
@@ -3043,8 +3046,7 @@ async function ensureCalendarConnected(ctx, options) {
|
|
|
3043
3046
|
async function lifecycleContext(options, optionalProjectCapabilities = []) {
|
|
3044
3047
|
const cfg = await loadProjectConfig(options.configPath);
|
|
3045
3048
|
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
3046
|
-
const env =
|
|
3047
|
-
if (!env || !cfg.envs.includes(env)) throw new Error(`env "${env ?? ""}" is not declared in ${options.configPath}`);
|
|
3049
|
+
const env = resolveEnv(cfg, options.env);
|
|
3048
3050
|
calendarServiceConfig(cfg, env);
|
|
3049
3051
|
const out = options.stdout ?? console;
|
|
3050
3052
|
const doFetch = options.fetch ?? fetch;
|
|
@@ -3188,6 +3190,7 @@ var init_calendar = __esm({
|
|
|
3188
3190
|
init_handshake_approval();
|
|
3189
3191
|
init_token();
|
|
3190
3192
|
init_human_session();
|
|
3193
|
+
init_tenant();
|
|
3191
3194
|
}
|
|
3192
3195
|
});
|
|
3193
3196
|
|
|
@@ -3264,8 +3267,7 @@ var init_capabilities = __esm({
|
|
|
3264
3267
|
// src/ai-models.ts
|
|
3265
3268
|
async function aiModels(options = {}) {
|
|
3266
3269
|
const cfg = await loadProjectConfig(options.configPath ?? "odla.config.mjs");
|
|
3267
|
-
const env = options.env
|
|
3268
|
-
if (!cfg.envs.includes(env)) throw new Error(`ai models env "${env}" is not declared in config envs`);
|
|
3270
|
+
const env = resolveEnv(cfg, options.env);
|
|
3269
3271
|
const url = new URL(`/registry/apps/${encodeURIComponent(cfg.app.id)}/public-config`, cfg.platformUrl);
|
|
3270
3272
|
url.searchParams.set("env", env);
|
|
3271
3273
|
const response2 = await (options.fetch ?? fetch)(url);
|
|
@@ -3323,6 +3325,7 @@ var init_ai_models = __esm({
|
|
|
3323
3325
|
init_cjs_shims();
|
|
3324
3326
|
import_ai = require("@odla-ai/ai");
|
|
3325
3327
|
init_config();
|
|
3328
|
+
init_tenant();
|
|
3326
3329
|
}
|
|
3327
3330
|
});
|
|
3328
3331
|
|
|
@@ -4950,10 +4953,7 @@ async function secretsPushAfterPreflight(options) {
|
|
|
4950
4953
|
async function secretsPushImpl(options, preflight) {
|
|
4951
4954
|
const out = options.stdout ?? console;
|
|
4952
4955
|
const cfg = await loadProjectConfig(options.configPath);
|
|
4953
|
-
const env = options.env;
|
|
4954
|
-
if (!cfg.envs.includes(env)) {
|
|
4955
|
-
throw new Error(`env "${env}" is not in config envs (${cfg.envs.join(", ")})`);
|
|
4956
|
-
}
|
|
4956
|
+
const env = resolveEnv(cfg, options.env);
|
|
4957
4957
|
if (PROD_ENV_NAMES.has(env) && !options.yes) {
|
|
4958
4958
|
throw new Error(`refusing to push a secret to "${env}" without --yes`);
|
|
4959
4959
|
}
|
|
@@ -5019,6 +5019,7 @@ var init_secrets = __esm({
|
|
|
5019
5019
|
init_config();
|
|
5020
5020
|
init_local();
|
|
5021
5021
|
init_redact();
|
|
5022
|
+
init_tenant();
|
|
5022
5023
|
init_wrangler();
|
|
5023
5024
|
PROD_ENV_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
5024
5025
|
}
|
|
@@ -5072,14 +5073,12 @@ async function resolveVaultWrite(options) {
|
|
|
5072
5073
|
const out = options.stdout ?? console;
|
|
5073
5074
|
const doFetch = options.fetch ?? fetch;
|
|
5074
5075
|
const cfg = await loadProjectConfig(options.configPath);
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
if (PROD_ENV_NAMES2.has(options.env) && !options.yes) {
|
|
5079
|
-
throw new Error(`refusing to store a secret for "${options.env}" without --yes`);
|
|
5076
|
+
const env = resolveEnv(cfg, options.env);
|
|
5077
|
+
if (PROD_ENV_NAMES2.has(env) && !options.yes) {
|
|
5078
|
+
throw new Error(`refusing to store a secret for "${env}" without --yes`);
|
|
5080
5079
|
}
|
|
5081
5080
|
const value2 = await secretInputValue(options, "secret");
|
|
5082
|
-
return { cfg, tenantId: (0, import_apps10.tenantIdFor)(cfg.app.id,
|
|
5081
|
+
return { cfg, tenantId: (0, import_apps10.tenantIdFor)(cfg.app.id, env), value: value2, doFetch, out };
|
|
5083
5082
|
}
|
|
5084
5083
|
var import_ai3, import_apps10, PROD_ENV_NAMES2;
|
|
5085
5084
|
var init_secrets_set = __esm({
|
|
@@ -5092,6 +5091,7 @@ var init_secrets_set = __esm({
|
|
|
5092
5091
|
init_redact();
|
|
5093
5092
|
init_secret_input();
|
|
5094
5093
|
init_token();
|
|
5094
|
+
init_tenant();
|
|
5095
5095
|
PROD_ENV_NAMES2 = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
5096
5096
|
}
|
|
5097
5097
|
});
|
|
@@ -5393,8 +5393,7 @@ var init_skill = __esm({
|
|
|
5393
5393
|
async function smoke(options) {
|
|
5394
5394
|
const out = options.stdout ?? console;
|
|
5395
5395
|
const cfg = await loadProjectConfig(options.configPath);
|
|
5396
|
-
const env =
|
|
5397
|
-
if (!cfg.envs.includes(env)) throw new Error(`env "${env}" is not declared in ${displayPath(cfg.configPath, cfg.rootDir)}`);
|
|
5396
|
+
const env = resolveEnv(cfg, options.env);
|
|
5398
5397
|
const credentials = readCredentials(cfg.local.credentialsFile);
|
|
5399
5398
|
if (!credentials) {
|
|
5400
5399
|
throw new Error(`local credentials missing: ${displayPath(cfg.local.credentialsFile, cfg.rootDir)}. Run "odla-ai provision --write-dev-vars".`);
|
|
@@ -5540,6 +5539,7 @@ var init_smoke = __esm({
|
|
|
5540
5539
|
init_integrations();
|
|
5541
5540
|
init_redact();
|
|
5542
5541
|
init_token();
|
|
5542
|
+
init_tenant();
|
|
5543
5543
|
}
|
|
5544
5544
|
});
|
|
5545
5545
|
|
|
@@ -9623,7 +9623,7 @@ Usage:
|
|
|
9623
9623
|
odla-ai security report <job-id> [--json]
|
|
9624
9624
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
9625
9625
|
odla-ai security run [target] --self --ack-redacted-source
|
|
9626
|
-
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]
|
|
9626
|
+
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]
|
|
9627
9627
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--email <odla-account>] [--no-open]
|
|
9628
9628
|
odla-ai skill install [--dir <project>] [--agent <name>] [--global] [--force]
|
|
9629
9629
|
odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
|
|
@@ -9725,6 +9725,9 @@ Commands:
|
|
|
9725
9725
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
9726
9726
|
explicit unknowns, and next actions through a read-only grant.
|
|
9727
9727
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
9728
|
+
"provision --live --yes" initializes only the live instance of
|
|
9729
|
+
an existing sandbox app and enables every configured service;
|
|
9730
|
+
no edit to the dev-first envs list is required.
|
|
9728
9731
|
smoke Verify credentials, public-config, composed schema, db aggregate, and integration probes.
|
|
9729
9732
|
skill Same installer; --agent accepts all, claude, codex, cursor,
|
|
9730
9733
|
copilot, gemini, or agents (repeatable or comma-separated).
|
|
@@ -9737,8 +9740,10 @@ Safety:
|
|
|
9737
9740
|
Every app has two databases on odla.ai: a sandbox (env "dev", tenant
|
|
9738
9741
|
<appId>--dev) and a live one (env "prod", tenant <appId>). Both are served by
|
|
9739
9742
|
production odla.ai \u2014 there is no separate odla to point at. New projects get
|
|
9740
|
-
the sandbox only.
|
|
9741
|
-
|
|
9743
|
+
the sandbox only. Run "provision --live --dry-run", then "provision --live
|
|
9744
|
+
--yes" to initialize live from that existing sandbox project's config. Add
|
|
9745
|
+
"prod" to envs only when ordinary provision runs should manage both; use
|
|
9746
|
+
--dry-run first to inspect either resolved plan.
|
|
9742
9747
|
A real provision first verifies both the released CLI version and the exact
|
|
9743
9748
|
pinned versions of every external @odla-ai runtime module before importing
|
|
9744
9749
|
the command graph. A stale workspace module blocks with its resolved path and
|
|
@@ -11850,10 +11855,39 @@ var init_provision_credentials = __esm({
|
|
|
11850
11855
|
}
|
|
11851
11856
|
});
|
|
11852
11857
|
|
|
11858
|
+
// src/provision-live.ts
|
|
11859
|
+
function liveProvisionConfig(cfg) {
|
|
11860
|
+
if (!cfg.envs.includes("dev")) {
|
|
11861
|
+
throw new Error(
|
|
11862
|
+
`--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`
|
|
11863
|
+
);
|
|
11864
|
+
}
|
|
11865
|
+
if (cfg.services.includes("calendar")) {
|
|
11866
|
+
const google = cfg.calendar?.google;
|
|
11867
|
+
const availability = google?.availabilityCalendars ?? google?.calendars;
|
|
11868
|
+
if (!availability?.prod?.length) {
|
|
11869
|
+
throw new Error(
|
|
11870
|
+
`--live requires calendar.google.availabilityCalendars.prod in ${cfg.configPath}; live uses a separate Google Calendar connection, so sandbox calendar ids are not copied`
|
|
11871
|
+
);
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
return { ...cfg, envs: ["prod"] };
|
|
11875
|
+
}
|
|
11876
|
+
var init_provision_live = __esm({
|
|
11877
|
+
"src/provision-live.ts"() {
|
|
11878
|
+
"use strict";
|
|
11879
|
+
init_cjs_shims();
|
|
11880
|
+
}
|
|
11881
|
+
});
|
|
11882
|
+
|
|
11853
11883
|
// src/provision.ts
|
|
11854
11884
|
async function provision(options) {
|
|
11855
11885
|
const out = options.stdout ?? console;
|
|
11856
|
-
const
|
|
11886
|
+
const loaded = await loadProjectConfig(
|
|
11887
|
+
options.configPath,
|
|
11888
|
+
options.live ? { additionalEnvs: ["prod"] } : void 0
|
|
11889
|
+
);
|
|
11890
|
+
const cfg = options.live ? liveProvisionConfig(loaded) : loaded;
|
|
11857
11891
|
const plan = buildPlan(cfg);
|
|
11858
11892
|
const hasDb = cfg.services.includes("db");
|
|
11859
11893
|
const hasO11y = cfg.services.includes("o11y");
|
|
@@ -11867,6 +11901,7 @@ async function provision(options) {
|
|
|
11867
11901
|
out.log(` platform: ${plan.platformUrl}`);
|
|
11868
11902
|
out.log(` db: ${plan.dbEndpoint}`);
|
|
11869
11903
|
out.log(` envs: ${plan.envs.join(", ")}`);
|
|
11904
|
+
if (options.live) out.log(" target: live setup (existing sandbox app)");
|
|
11870
11905
|
out.log(` services: ${plan.services.join(", ")}`);
|
|
11871
11906
|
out.log(` integrations: ${plan.integrations.length ? plan.integrations.join(", ") : "none"}`);
|
|
11872
11907
|
const productionEnvs = plan.envs.filter((env) => env === "prod" || env === "production");
|
|
@@ -11927,6 +11962,10 @@ async function provision(options) {
|
|
|
11927
11962
|
const existing = await apps.resolveApp(cfg.app.id);
|
|
11928
11963
|
if (existing) {
|
|
11929
11964
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
11965
|
+
} else if (options.live) {
|
|
11966
|
+
throw new Error(
|
|
11967
|
+
`cannot set up live for "${cfg.app.id}": the sandbox app does not exist yet; run "odla-ai provision" first`
|
|
11968
|
+
);
|
|
11930
11969
|
} else {
|
|
11931
11970
|
try {
|
|
11932
11971
|
await apps.createApp({ name: cfg.app.name, appId: cfg.app.id });
|
|
@@ -12077,6 +12116,7 @@ var init_provision = __esm({
|
|
|
12077
12116
|
init_token();
|
|
12078
12117
|
init_local();
|
|
12079
12118
|
init_provision_credentials();
|
|
12119
|
+
init_provision_live();
|
|
12080
12120
|
init_provision_helpers();
|
|
12081
12121
|
init_secrets();
|
|
12082
12122
|
}
|
|
@@ -14049,6 +14089,7 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
14049
14089
|
async function provisionCommand(parsed, dependencies) {
|
|
14050
14090
|
assertArgs(parsed, [
|
|
14051
14091
|
"config",
|
|
14092
|
+
"live",
|
|
14052
14093
|
"dry-run",
|
|
14053
14094
|
"rotate-keys",
|
|
14054
14095
|
"rotate-o11y-token",
|
|
@@ -14065,6 +14106,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
14065
14106
|
const writeDevVars2 = parsed.options["write-dev-vars"];
|
|
14066
14107
|
const options = {
|
|
14067
14108
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
14109
|
+
live: parsed.options.live === true,
|
|
14068
14110
|
dryRun: parsed.options["dry-run"] === true,
|
|
14069
14111
|
rotateKeys: parsed.options["rotate-keys"] === true,
|
|
14070
14112
|
rotateO11yToken: parsed.options["rotate-o11y-token"] === true,
|