@odla-ai/cli 0.29.0 → 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 +42 -23
- package/dist/bin.cjs +254 -43
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-YTVLTADT.js → chunk-PPSXHZJI.js} +234 -44
- package/dist/chunk-PPSXHZJI.js.map +1 -0
- package/dist/{cli-DNOAYDBR.js → cli-CKBUVTJM.js} +2 -2
- package/dist/index.cjs +233 -43
- 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/dist/runtime/pi-agent.js +18728 -15
- package/package.json +1 -1
- package/skills/odla/SKILL.md +4 -1
- 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-YTVLTADT.js.map +0 -1
- /package/dist/{cli-DNOAYDBR.js.map → cli-CKBUVTJM.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runCli
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PPSXHZJI.js";
|
|
5
5
|
import {
|
|
6
6
|
exitCodeFor
|
|
7
7
|
} from "./chunk-UKLSRQ5J.js";
|
|
@@ -9,4 +9,4 @@ export {
|
|
|
9
9
|
exitCodeFor,
|
|
10
10
|
runCli
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=cli-
|
|
12
|
+
//# sourceMappingURL=cli-CKBUVTJM.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -3961,6 +3961,41 @@ async function wranglerLoggedIn(run, cwd) {
|
|
|
3961
3961
|
return false;
|
|
3962
3962
|
}
|
|
3963
3963
|
}
|
|
3964
|
+
async function wranglerRuntimeTarget(run, opts) {
|
|
3965
|
+
const configPath = findWranglerConfig(opts.cwd);
|
|
3966
|
+
if (!configPath) throw new Error(`no wrangler config found in ${opts.cwd}`);
|
|
3967
|
+
const config = readWranglerConfig(configPath);
|
|
3968
|
+
if (!config) throw new Error("runtime credential delivery requires wrangler.json or wrangler.jsonc");
|
|
3969
|
+
const baseName = typeof config.name === "string" ? config.name : "";
|
|
3970
|
+
const envs = config.env && typeof config.env === "object" && !Array.isArray(config.env) ? config.env : {};
|
|
3971
|
+
const envConfig = opts.env && envs[opts.env] && typeof envs[opts.env] === "object" ? envs[opts.env] : {};
|
|
3972
|
+
const explicitName = typeof envConfig.name === "string" ? envConfig.name : "";
|
|
3973
|
+
const serviceEnvironments = config.legacy_env === false;
|
|
3974
|
+
if (serviceEnvironments && explicitName) {
|
|
3975
|
+
throw new Error("env.<name>.name is not allowed when wrangler legacy_env is false");
|
|
3976
|
+
}
|
|
3977
|
+
const scriptName = serviceEnvironments ? baseName : explicitName || (opts.env ? `${baseName}-${opts.env}` : baseName);
|
|
3978
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,62}$/.test(scriptName)) {
|
|
3979
|
+
throw new Error("wrangler config must resolve an exact Worker name before credentials are issued");
|
|
3980
|
+
}
|
|
3981
|
+
const configuredAccount = typeof envConfig.account_id === "string" ? envConfig.account_id : typeof config.account_id === "string" ? config.account_id : "";
|
|
3982
|
+
const whoami = await run("npx", ["wrangler", "whoami"], { cwd: opts.cwd });
|
|
3983
|
+
if (whoami.code !== 0 || /not authenticated/i.test(`${whoami.stdout}${whoami.stderr}`)) {
|
|
3984
|
+
throw new Error(`wrangler is not logged in \u2014 run "wrangler login" (a browser step for the human)`);
|
|
3985
|
+
}
|
|
3986
|
+
const discovered = [...new Set(`${whoami.stdout}
|
|
3987
|
+
${whoami.stderr}`.match(/\b[a-f0-9]{32}\b/gi)?.map((id) => id.toLowerCase()) ?? [])];
|
|
3988
|
+
const accountId = configuredAccount.toLowerCase() || (discovered.length === 1 ? discovered[0] : "");
|
|
3989
|
+
if (!/^[a-f0-9]{32}$/.test(accountId) || configuredAccount && discovered.length > 0 && !discovered.includes(accountId)) {
|
|
3990
|
+
throw new Error("Wrangler account is ambiguous or does not match account_id in the config");
|
|
3991
|
+
}
|
|
3992
|
+
return {
|
|
3993
|
+
provider: "cloudflare",
|
|
3994
|
+
accountId,
|
|
3995
|
+
scriptName,
|
|
3996
|
+
...opts.env ? { environment: opts.env } : {}
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3964
3999
|
function wranglerPutSecret(run, opts) {
|
|
3965
4000
|
const args = [
|
|
3966
4001
|
"wrangler",
|
|
@@ -3972,6 +4007,16 @@ function wranglerPutSecret(run, opts) {
|
|
|
3972
4007
|
];
|
|
3973
4008
|
return run("npx", args, { input: opts.value, cwd: opts.cwd });
|
|
3974
4009
|
}
|
|
4010
|
+
function wranglerBulkSecrets(run, opts) {
|
|
4011
|
+
const args = [
|
|
4012
|
+
"wrangler",
|
|
4013
|
+
"secret",
|
|
4014
|
+
"bulk",
|
|
4015
|
+
...opts.env ? ["--env", opts.env] : [],
|
|
4016
|
+
...opts.configPath ? ["--config", opts.configPath] : []
|
|
4017
|
+
];
|
|
4018
|
+
return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
|
|
4019
|
+
}
|
|
3975
4020
|
|
|
3976
4021
|
// src/doctor-checks.ts
|
|
3977
4022
|
function lintRules(rules, entities, publicRead) {
|
|
@@ -4460,9 +4505,6 @@ var PROD_ENV_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
|
4460
4505
|
async function secretsPush(options) {
|
|
4461
4506
|
await secretsPushImpl(options, true);
|
|
4462
4507
|
}
|
|
4463
|
-
async function secretsPushAfterPreflight(options) {
|
|
4464
|
-
await secretsPushImpl(options, false);
|
|
4465
|
-
}
|
|
4466
4508
|
async function secretsPushImpl(options, preflight) {
|
|
4467
4509
|
const out = options.stdout ?? console;
|
|
4468
4510
|
const cfg = await loadProjectConfig(options.configPath);
|
|
@@ -8827,6 +8869,55 @@ function requireName(parsed) {
|
|
|
8827
8869
|
return name;
|
|
8828
8870
|
}
|
|
8829
8871
|
|
|
8872
|
+
// src/credential-command.ts
|
|
8873
|
+
async function responseError(response2) {
|
|
8874
|
+
return redactSecrets((await response2.text()).slice(0, 1e3));
|
|
8875
|
+
}
|
|
8876
|
+
async function credentialCommand(parsed, deps = {}) {
|
|
8877
|
+
const action2 = parsed.positionals[1] ?? "list";
|
|
8878
|
+
if (action2 !== "list" && action2 !== "revoke") {
|
|
8879
|
+
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
8880
|
+
}
|
|
8881
|
+
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
8882
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
8883
|
+
const doFetch = deps.fetch ?? fetch;
|
|
8884
|
+
const out = deps.stdout ?? console;
|
|
8885
|
+
const token = await getDeveloperToken(cfg, {
|
|
8886
|
+
configPath: cfg.configPath,
|
|
8887
|
+
token: stringOpt(parsed.options.token),
|
|
8888
|
+
email: stringOpt(parsed.options.email),
|
|
8889
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
8890
|
+
openApprovalUrl: deps.openUrl
|
|
8891
|
+
}, doFetch, out, { optionalProjectCapabilities: ["app.manage"] });
|
|
8892
|
+
const base = `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials`;
|
|
8893
|
+
if (action2 === "revoke") {
|
|
8894
|
+
const id = parsed.positionals[2];
|
|
8895
|
+
if (!id) throw new Error("credentials revoke requires the exact receipt id from credentials list");
|
|
8896
|
+
const response3 = await doFetch(`${base}/${encodeURIComponent(id)}`, {
|
|
8897
|
+
method: "DELETE",
|
|
8898
|
+
headers: { authorization: `Bearer ${token}` }
|
|
8899
|
+
});
|
|
8900
|
+
if (!response3.ok) throw new Error(`runtime credential revoke failed (${response3.status}): ${await responseError(response3)}`);
|
|
8901
|
+
const body2 = await response3.json();
|
|
8902
|
+
return out.log(parsed.options.json === true ? JSON.stringify(body2, null, 2) : `revoked ${body2.receipt.id} (${body2.receipt.env} ${body2.receipt.target.scriptName})`);
|
|
8903
|
+
}
|
|
8904
|
+
const query = new URLSearchParams();
|
|
8905
|
+
const requestedEnv = stringOpt(parsed.options.env);
|
|
8906
|
+
if (requestedEnv) query.set("env", requestedEnv);
|
|
8907
|
+
const response2 = await doFetch(`${base}${query.size ? `?${query}` : ""}`, {
|
|
8908
|
+
headers: { authorization: `Bearer ${token}` }
|
|
8909
|
+
});
|
|
8910
|
+
if (!response2.ok) throw new Error(`runtime credential inventory failed (${response2.status}): ${await responseError(response2)}`);
|
|
8911
|
+
const body = await response2.json();
|
|
8912
|
+
const credentials = parsed.options.all === true ? body.credentials : body.credentials.filter((item) => item.state === "committed");
|
|
8913
|
+
if (parsed.options.json === true) return out.log(JSON.stringify({ credentials }, null, 2));
|
|
8914
|
+
if (!credentials.length) return out.log("no active runtime credential receipts");
|
|
8915
|
+
out.log("RECEIPT ENV TARGET CREATED");
|
|
8916
|
+
for (const item of credentials) {
|
|
8917
|
+
out.log(`${item.id} ${item.env} ${item.target.accountId ?? "local"}/${item.target.scriptName} ${new Date(item.createdAt).toISOString()}`);
|
|
8918
|
+
}
|
|
8919
|
+
}
|
|
8920
|
+
|
|
8830
8921
|
// src/help-usage.ts
|
|
8831
8922
|
var USAGE_SECTION = `
|
|
8832
8923
|
Start here:
|
|
@@ -8945,6 +9036,8 @@ Usage:
|
|
|
8945
9036
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
8946
9037
|
odla-ai security run [target] --self --ack-redacted-source
|
|
8947
9038
|
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]
|
|
9039
|
+
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
9040
|
+
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
8948
9041
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--email <odla-account>] [--no-open]
|
|
8949
9042
|
odla-ai skill install [--dir <project>] [--agent <name>] [--global] [--force]
|
|
8950
9043
|
odla-ai secrets push --env <env> [--config odla.config.mjs] [--dry-run] [--yes]
|
|
@@ -9067,9 +9160,13 @@ Safety:
|
|
|
9067
9160
|
pinned versions of every external @odla-ai runtime module before importing
|
|
9068
9161
|
the command graph. A stale workspace module blocks with its resolved path and
|
|
9069
9162
|
tells the agent to update/rebase, npm ci, and rebuild.
|
|
9070
|
-
Provision caches the approved developer token
|
|
9071
|
-
|
|
9072
|
-
|
|
9163
|
+
Provision caches the approved developer token under .odla/ with mode 0600.
|
|
9164
|
+
Local-only development provisioning may also cache that developer's service
|
|
9165
|
+
credentials there. \`provision --push-secrets\` instead stages a fresh,
|
|
9166
|
+
independently revocable credential set through the approved handshake,
|
|
9167
|
+
transfers the complete set to the exact Worker with \`wrangler secret bulk\`
|
|
9168
|
+
over stdin, and never writes its plaintext under .odla. Secret push preflights
|
|
9169
|
+
Wrangler before shown-once issuance; it never rotates sibling credentials.
|
|
9073
9170
|
Projectless PM, Discussions, o11y, runbook, and identity commands use
|
|
9074
9171
|
--platform/--app/--env, ODLA_PLATFORM_URL/ODLA_APP_ID/ODLA_ENV, and
|
|
9075
9172
|
ODLA_DEV_TOKEN. Save non-secret scope metadata with "context save", then
|
|
@@ -10993,7 +11090,7 @@ async function issueO11yToken(opts) {
|
|
|
10993
11090
|
);
|
|
10994
11091
|
if (res.status === 409 && !opts.rotateO11y) {
|
|
10995
11092
|
throw new Error(
|
|
10996
|
-
`o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --
|
|
11093
|
+
`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`
|
|
10997
11094
|
);
|
|
10998
11095
|
}
|
|
10999
11096
|
if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText7(res)}`);
|
|
@@ -11009,6 +11106,88 @@ async function safeText7(res) {
|
|
|
11009
11106
|
}
|
|
11010
11107
|
}
|
|
11011
11108
|
|
|
11109
|
+
// src/runtime-credentials.ts
|
|
11110
|
+
var import_node_crypto5 = require("crypto");
|
|
11111
|
+
function runtimeUrl(cfg, suffix = "") {
|
|
11112
|
+
return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
|
|
11113
|
+
}
|
|
11114
|
+
async function safeError(response2) {
|
|
11115
|
+
const text2 = await response2.text();
|
|
11116
|
+
return redactSecrets(text2.slice(0, 1e3));
|
|
11117
|
+
}
|
|
11118
|
+
async function finish(doFetch, cfg, token, sessionId, method) {
|
|
11119
|
+
return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
|
|
11120
|
+
method,
|
|
11121
|
+
headers: { authorization: `Bearer ${token}` }
|
|
11122
|
+
});
|
|
11123
|
+
}
|
|
11124
|
+
async function deliverRuntimeCredentials(cfg, options) {
|
|
11125
|
+
const doFetch = options.fetch ?? fetch;
|
|
11126
|
+
const run = options.runner ?? defaultRunner;
|
|
11127
|
+
const wranglerEnv = options.env === "prod" || options.env === "production" ? void 0 : options.env;
|
|
11128
|
+
const target = await wranglerRuntimeTarget(run, {
|
|
11129
|
+
cwd: cfg.rootDir,
|
|
11130
|
+
env: wranglerEnv
|
|
11131
|
+
});
|
|
11132
|
+
const issue = await doFetch(runtimeUrl(cfg), {
|
|
11133
|
+
method: "POST",
|
|
11134
|
+
headers: {
|
|
11135
|
+
authorization: `Bearer ${options.developerToken}`,
|
|
11136
|
+
"content-type": "application/json"
|
|
11137
|
+
},
|
|
11138
|
+
body: JSON.stringify({
|
|
11139
|
+
env: options.env,
|
|
11140
|
+
idempotencyKey: `wrangler:${(0, import_node_crypto5.randomUUID)()}`,
|
|
11141
|
+
target
|
|
11142
|
+
})
|
|
11143
|
+
});
|
|
11144
|
+
const body = await issue.json().catch(() => null);
|
|
11145
|
+
if (!issue.ok || typeof body?.sessionId !== "string" || !body.secrets || typeof body.secrets !== "object") {
|
|
11146
|
+
const detail = body?.error?.code ?? body?.error?.message ?? issue.status;
|
|
11147
|
+
throw new Error(`runtime credential staging failed: ${String(detail)}`);
|
|
11148
|
+
}
|
|
11149
|
+
const secrets = body.secrets;
|
|
11150
|
+
const values = {};
|
|
11151
|
+
for (const name of ["ODLA_API_KEY", "ODLA_O11Y_TOKEN"]) {
|
|
11152
|
+
const value2 = secrets[name];
|
|
11153
|
+
if (typeof value2 === "string" && value2) values[name] = value2;
|
|
11154
|
+
}
|
|
11155
|
+
if (Object.keys(values).length === 0) {
|
|
11156
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11157
|
+
throw new Error("runtime credential staging returned no configured service credentials");
|
|
11158
|
+
}
|
|
11159
|
+
const pushed = await wranglerBulkSecrets(run, {
|
|
11160
|
+
secrets: values,
|
|
11161
|
+
env: wranglerEnv,
|
|
11162
|
+
cwd: cfg.rootDir
|
|
11163
|
+
});
|
|
11164
|
+
if (pushed.code !== 0) {
|
|
11165
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11166
|
+
const detail = redactSecrets(`${pushed.stderr || pushed.stdout}`.trim());
|
|
11167
|
+
throw new Error(`wrangler secret bulk failed (exit ${pushed.code}): ${detail}`);
|
|
11168
|
+
}
|
|
11169
|
+
const committed = await finish(
|
|
11170
|
+
doFetch,
|
|
11171
|
+
cfg,
|
|
11172
|
+
options.developerToken,
|
|
11173
|
+
body.sessionId,
|
|
11174
|
+
"POST"
|
|
11175
|
+
);
|
|
11176
|
+
if (!committed.ok) {
|
|
11177
|
+
await finish(doFetch, cfg, options.developerToken, body.sessionId, "DELETE");
|
|
11178
|
+
throw new Error(`runtime credential receipt commit failed: ${await safeError(committed)}`);
|
|
11179
|
+
}
|
|
11180
|
+
options.stdout?.log(
|
|
11181
|
+
`${options.env}: installed ${Object.keys(values).join(" + ")} on ${target.accountId}/${target.scriptName}${target.environment ? ` (${target.environment})` : ""}; plaintext stayed in memory`
|
|
11182
|
+
);
|
|
11183
|
+
return {
|
|
11184
|
+
sessionId: body.sessionId,
|
|
11185
|
+
target,
|
|
11186
|
+
...values.ODLA_API_KEY ? { dbKey: values.ODLA_API_KEY } : {},
|
|
11187
|
+
...values.ODLA_O11Y_TOKEN ? { o11yToken: values.ODLA_O11Y_TOKEN } : {}
|
|
11188
|
+
};
|
|
11189
|
+
}
|
|
11190
|
+
|
|
11012
11191
|
// src/provision-live.ts
|
|
11013
11192
|
function liveProvisionConfig(cfg) {
|
|
11014
11193
|
if (!cfg.envs.includes("dev")) {
|
|
@@ -11042,8 +11221,10 @@ async function provision(options) {
|
|
|
11042
11221
|
if (options.rotateO11yToken && !hasO11y) {
|
|
11043
11222
|
throw new Error("--rotate-o11y-token requires the o11y service in odla.config.mjs");
|
|
11044
11223
|
}
|
|
11045
|
-
if (options.pushSecrets && options.
|
|
11046
|
-
throw new Error(
|
|
11224
|
+
if (options.pushSecrets && (options.rotateKeys || options.rotateO11yToken)) {
|
|
11225
|
+
throw new Error(
|
|
11226
|
+
"--push-secrets always issues an independent runtime credential set; do not combine it with destructive rotation flags"
|
|
11227
|
+
);
|
|
11047
11228
|
}
|
|
11048
11229
|
out.log(`odla-ai: ${plan.appName} (${plan.appId})`);
|
|
11049
11230
|
out.log(` platform: ${plan.platformUrl}`);
|
|
@@ -11080,7 +11261,7 @@ async function provision(options) {
|
|
|
11080
11261
|
out.log(` secrets: ${options.pushSecrets ? "push configured Worker secrets" : "local only"}`);
|
|
11081
11262
|
return;
|
|
11082
11263
|
}
|
|
11083
|
-
let credentials = readCredentials(cfg.local.credentialsFile);
|
|
11264
|
+
let credentials = options.pushSecrets ? null : readCredentials(cfg.local.credentialsFile);
|
|
11084
11265
|
if (credentials && credentials.appId !== cfg.app.id) {
|
|
11085
11266
|
throw new Error(
|
|
11086
11267
|
`credentials at ${displayPath(cfg.local.credentialsFile, cfg.rootDir)} are for "${credentials.appId}", not "${cfg.app.id}"`
|
|
@@ -11089,7 +11270,7 @@ async function provision(options) {
|
|
|
11089
11270
|
const rotatesO11y = !!(options.rotateKeys || options.rotateO11yToken);
|
|
11090
11271
|
const missingO11y = cfg.envs.some((env) => !credentials?.envs[env]?.o11yToken);
|
|
11091
11272
|
const missingDb = cfg.envs.some((env) => !credentials?.envs[env]?.dbKey);
|
|
11092
|
-
const losesShownOnceCredential = hasDb && (!!options.rotateKeys || missingDb) || hasO11y && (rotatesO11y || missingO11y);
|
|
11273
|
+
const losesShownOnceCredential = !options.pushSecrets && (hasDb && (!!options.rotateKeys || missingDb) || hasO11y && (rotatesO11y || missingO11y));
|
|
11093
11274
|
if (options.writeCredentials === false && losesShownOnceCredential) {
|
|
11094
11275
|
throw new Error("credential issuance/rotation requires the private credentials file; remove --no-write-credentials");
|
|
11095
11276
|
}
|
|
@@ -11163,38 +11344,42 @@ async function provision(options) {
|
|
|
11163
11344
|
await applyCalendarBookingPage(calendarCtx, calendarBookingPageUrl(cfg, env), out);
|
|
11164
11345
|
}
|
|
11165
11346
|
}
|
|
11347
|
+
let devVarsCredentials = credentials;
|
|
11166
11348
|
for (const env of cfg.envs) {
|
|
11167
11349
|
const tenantId = (0, import_apps12.tenantIdFor)(cfg.app.id, env);
|
|
11168
|
-
|
|
11169
|
-
cfg,
|
|
11170
|
-
env,
|
|
11171
|
-
developerToken: token,
|
|
11172
|
-
credentials,
|
|
11173
|
-
rotateDb: !!options.rotateKeys,
|
|
11174
|
-
rotateO11y: rotatesO11y,
|
|
11175
|
-
write: options.writeCredentials !== false,
|
|
11176
|
-
fetch: doFetch,
|
|
11177
|
-
stdout: out
|
|
11178
|
-
});
|
|
11179
|
-
const dbKey = credentials.envs[env]?.dbKey;
|
|
11350
|
+
let dbKey;
|
|
11180
11351
|
if (options.pushSecrets) {
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
}
|
|
11352
|
+
const delivered = await deliverRuntimeCredentials(cfg, {
|
|
11353
|
+
env,
|
|
11354
|
+
developerToken: token,
|
|
11355
|
+
fetch: doFetch,
|
|
11356
|
+
runner: options.secretRunner,
|
|
11357
|
+
stdout: out
|
|
11358
|
+
});
|
|
11359
|
+
dbKey = delivered.dbKey;
|
|
11360
|
+
devVarsCredentials = mergeCredential(devVarsCredentials, {
|
|
11361
|
+
appId: cfg.app.id,
|
|
11362
|
+
platformUrl: cfg.platformUrl,
|
|
11363
|
+
dbEndpoint: cfg.dbEndpoint,
|
|
11364
|
+
env,
|
|
11365
|
+
tenantId,
|
|
11366
|
+
...delivered.dbKey ? { dbKey: delivered.dbKey } : {},
|
|
11367
|
+
...delivered.o11yToken ? { o11yToken: delivered.o11yToken } : {}
|
|
11368
|
+
});
|
|
11369
|
+
} else {
|
|
11370
|
+
credentials = await provisionEnvCredentials({
|
|
11371
|
+
cfg,
|
|
11372
|
+
env,
|
|
11373
|
+
developerToken: token,
|
|
11374
|
+
credentials,
|
|
11375
|
+
rotateDb: !!options.rotateKeys,
|
|
11376
|
+
rotateO11y: rotatesO11y,
|
|
11377
|
+
write: options.writeCredentials !== false,
|
|
11378
|
+
fetch: doFetch,
|
|
11379
|
+
stdout: out
|
|
11380
|
+
});
|
|
11381
|
+
devVarsCredentials = credentials;
|
|
11382
|
+
dbKey = credentials.envs[env]?.dbKey;
|
|
11198
11383
|
}
|
|
11199
11384
|
if (schema && dbKey) {
|
|
11200
11385
|
await postJson(doFetch, `${cfg.dbEndpoint}/app/${encodeURIComponent(tenantId)}/schema`, dbKey, { schema });
|
|
@@ -11218,13 +11403,13 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11218
11403
|
}
|
|
11219
11404
|
}
|
|
11220
11405
|
}
|
|
11221
|
-
if (options.writeCredentials !== false && credentials) {
|
|
11406
|
+
if (!options.pushSecrets && options.writeCredentials !== false && credentials) {
|
|
11222
11407
|
const ignored = cfg.local.gitignore && gitignoreEntry(cfg.rootDir, cfg.local.credentialsFile);
|
|
11223
11408
|
out.log(`credentials: wrote ${displayPath(cfg.local.credentialsFile, cfg.rootDir)} (0600${ignored ? ", gitignored" : ""})`);
|
|
11224
11409
|
}
|
|
11225
|
-
if (devVarsTarget &&
|
|
11410
|
+
if (devVarsTarget && devVarsCredentials) {
|
|
11226
11411
|
const env = cfg.envs.includes("dev") ? "dev" : cfg.envs[0] ?? "prod";
|
|
11227
|
-
writeDevVars(devVarsTarget,
|
|
11412
|
+
writeDevVars(devVarsTarget, devVarsCredentials, env, o11yDevVars(cfg));
|
|
11228
11413
|
out.log(`dev vars: wrote ${displayPath(devVarsTarget, cfg.rootDir)} for ${env}`);
|
|
11229
11414
|
}
|
|
11230
11415
|
if (cfg.services.includes("calendar")) {
|
|
@@ -11315,6 +11500,7 @@ var COMMAND_SURFACE = {
|
|
|
11315
11500
|
code: { connect: {} },
|
|
11316
11501
|
config: { diff: {}, plan: {}, apply: {} },
|
|
11317
11502
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
11503
|
+
credentials: { list: {}, revoke: {} },
|
|
11318
11504
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
11319
11505
|
discuss: {
|
|
11320
11506
|
groups: {},
|
|
@@ -13052,6 +13238,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
13052
13238
|
await contextCommand(parsed, runtime);
|
|
13053
13239
|
return;
|
|
13054
13240
|
}
|
|
13241
|
+
if (command === "credentials") {
|
|
13242
|
+
await credentialCommand(parsed, runtime);
|
|
13243
|
+
return;
|
|
13244
|
+
}
|
|
13055
13245
|
if (command === "runbook") {
|
|
13056
13246
|
await runbookCommand(parsed, runtime);
|
|
13057
13247
|
return;
|