@odla-ai/cli 0.25.18 → 0.25.19
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 +10 -8
- package/dist/bin.cjs +392 -385
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-B6DISJPC.js → chunk-S3EJ66VG.js} +280 -273
- package/dist/chunk-S3EJ66VG.js.map +1 -0
- package/dist/index.cjs +392 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-B6DISJPC.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -6033,15 +6033,16 @@ Usage:
|
|
|
6033
6033
|
odla-ai runbook rm <slug> [--app <id>]
|
|
6034
6034
|
odla-ai capabilities [--json]
|
|
6035
6035
|
odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
|
|
6036
|
-
odla-ai admin ai show [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6037
|
-
odla-ai admin ai models [--provider <id>] [--json]
|
|
6038
|
-
odla-ai admin ai set <purpose> [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
odla-ai admin ai
|
|
6043
|
-
odla-ai admin ai
|
|
6044
|
-
odla-ai admin ai
|
|
6036
|
+
odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6037
|
+
odla-ai admin ai models [--context <name>] [--provider <id>] [--json]
|
|
6038
|
+
odla-ai admin ai set <purpose> [--context <name>] [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6039
|
+
[--max-input-bytes <n>] [--max-output-tokens <n>] [--max-calls-per-run <n>] [--json]
|
|
6040
|
+
odla-ai admin ai set security [--context <name>] --discovery-provider <id> --discovery-model <id>
|
|
6041
|
+
--validation-provider <id> --validation-model <id> [--enabled|--no-enabled] [--json]
|
|
6042
|
+
odla-ai admin ai credentials [--context <name>] [--json]
|
|
6043
|
+
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
6044
|
+
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
6045
|
+
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
6045
6046
|
odla-ai security github connect [--repo owner/name] [--env dev] [--email <odla-account>] [--no-open]
|
|
6046
6047
|
odla-ai security github disconnect --source <id> [--env dev] [--yes]
|
|
6047
6048
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -7328,8 +7329,250 @@ function addOption(options, name, value) {
|
|
|
7328
7329
|
else options[name] = [String(current), String(value)];
|
|
7329
7330
|
}
|
|
7330
7331
|
|
|
7332
|
+
// src/operator-context.ts
|
|
7333
|
+
import { existsSync as existsSync10 } from "fs";
|
|
7334
|
+
import { join as join10, resolve as resolve11 } from "path";
|
|
7335
|
+
import process10 from "process";
|
|
7336
|
+
|
|
7337
|
+
// src/operator-profiles.ts
|
|
7338
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
7339
|
+
import { homedir as homedir2 } from "os";
|
|
7340
|
+
import { dirname as dirname7, join as join9, resolve as resolve10 } from "path";
|
|
7341
|
+
import process9 from "process";
|
|
7342
|
+
function operatorProfileFile() {
|
|
7343
|
+
return resolve10(
|
|
7344
|
+
clean(process9.env.ODLA_CONTEXT_FILE) ?? join9(homedir2(), ".odla", "contexts.json")
|
|
7345
|
+
);
|
|
7346
|
+
}
|
|
7347
|
+
function resolveOperatorProfile(parsed) {
|
|
7348
|
+
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
7349
|
+
const fromEnvironment = clean(process9.env.ODLA_CONTEXT);
|
|
7350
|
+
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7351
|
+
const file = operatorProfileFile();
|
|
7352
|
+
if (!name) {
|
|
7353
|
+
return { name: null, source: "unresolved", file, value: null };
|
|
7354
|
+
}
|
|
7355
|
+
assertOperatorName(name, "context");
|
|
7356
|
+
const profiles = readOperatorProfiles(file);
|
|
7357
|
+
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
7358
|
+
if (!value) {
|
|
7359
|
+
throw new Error(
|
|
7360
|
+
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
7361
|
+
);
|
|
7362
|
+
}
|
|
7363
|
+
return {
|
|
7364
|
+
name,
|
|
7365
|
+
source: fromFlag ? "flag" : "environment",
|
|
7366
|
+
file,
|
|
7367
|
+
value
|
|
7368
|
+
};
|
|
7369
|
+
}
|
|
7370
|
+
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
7371
|
+
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
7372
|
+
}
|
|
7373
|
+
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
7374
|
+
assertOperatorName(name, "context");
|
|
7375
|
+
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
7376
|
+
const profiles = readOperatorProfiles(file);
|
|
7377
|
+
profiles[name] = normalized;
|
|
7378
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7379
|
+
}
|
|
7380
|
+
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
7381
|
+
assertOperatorName(name, "context");
|
|
7382
|
+
const profiles = readOperatorProfiles(file);
|
|
7383
|
+
if (!Object.hasOwn(profiles, name)) return false;
|
|
7384
|
+
delete profiles[name];
|
|
7385
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7386
|
+
return true;
|
|
7387
|
+
}
|
|
7388
|
+
function operatorCredentialFiles(selection) {
|
|
7389
|
+
const base = selection.name ? join9(dirname7(selection.file), "profiles", selection.name) : join9(homedir2(), ".odla");
|
|
7390
|
+
return {
|
|
7391
|
+
developer: join9(base, "dev-token.json"),
|
|
7392
|
+
scoped: join9(base, "admin-token.local.json")
|
|
7393
|
+
};
|
|
7394
|
+
}
|
|
7395
|
+
function assertOperatorName(value, label) {
|
|
7396
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
7397
|
+
throw new Error(
|
|
7398
|
+
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
7399
|
+
);
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
function readOperatorProfiles(file) {
|
|
7403
|
+
if (!existsSync9(file)) return emptyProfiles();
|
|
7404
|
+
let raw;
|
|
7405
|
+
try {
|
|
7406
|
+
raw = JSON.parse(readFileSync7(file, "utf8"));
|
|
7407
|
+
} catch {
|
|
7408
|
+
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
7409
|
+
}
|
|
7410
|
+
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
7411
|
+
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
7412
|
+
}
|
|
7413
|
+
const unknown = Object.keys(raw).filter(
|
|
7414
|
+
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
7415
|
+
);
|
|
7416
|
+
if (unknown.length > 0) {
|
|
7417
|
+
throw new Error(
|
|
7418
|
+
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
7419
|
+
);
|
|
7420
|
+
}
|
|
7421
|
+
const profiles = emptyProfiles();
|
|
7422
|
+
for (const [name, value] of Object.entries(
|
|
7423
|
+
raw.profiles
|
|
7424
|
+
)) {
|
|
7425
|
+
assertOperatorName(name, "context");
|
|
7426
|
+
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
7427
|
+
}
|
|
7428
|
+
return profiles;
|
|
7429
|
+
}
|
|
7430
|
+
function emptyProfiles() {
|
|
7431
|
+
return /* @__PURE__ */ Object.create(null);
|
|
7432
|
+
}
|
|
7433
|
+
function validateProfile(value, label) {
|
|
7434
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7435
|
+
throw new Error(`${label} has an invalid shape`);
|
|
7436
|
+
}
|
|
7437
|
+
const unknown = Object.keys(value).filter(
|
|
7438
|
+
(key) => !["platform", "app", "environment"].includes(key)
|
|
7439
|
+
);
|
|
7440
|
+
if (unknown.length > 0) {
|
|
7441
|
+
throw new Error(
|
|
7442
|
+
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
7443
|
+
);
|
|
7444
|
+
}
|
|
7445
|
+
const candidate = value;
|
|
7446
|
+
if (typeof candidate.platform !== "string") {
|
|
7447
|
+
throw new Error(`${label} needs an absolute platform URL`);
|
|
7448
|
+
}
|
|
7449
|
+
const platform = platformAudience(candidate.platform);
|
|
7450
|
+
const app = clean(candidate.app);
|
|
7451
|
+
const environment2 = clean(candidate.environment);
|
|
7452
|
+
if (app) assertOperatorName(app, "app");
|
|
7453
|
+
if (environment2) assertOperatorName(environment2, "environment");
|
|
7454
|
+
return {
|
|
7455
|
+
platform,
|
|
7456
|
+
...app ? { app } : {},
|
|
7457
|
+
...environment2 ? { environment: environment2 } : {}
|
|
7458
|
+
};
|
|
7459
|
+
}
|
|
7460
|
+
function clean(value) {
|
|
7461
|
+
const normalized = value?.trim();
|
|
7462
|
+
return normalized || void 0;
|
|
7463
|
+
}
|
|
7464
|
+
|
|
7465
|
+
// src/operator-context.ts
|
|
7466
|
+
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
7467
|
+
async function resolveOperatorContext(parsed, options = {}) {
|
|
7468
|
+
const profile = resolveOperatorProfile(parsed);
|
|
7469
|
+
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
7470
|
+
const configPath = resolve11(configArgument);
|
|
7471
|
+
const explicitConfig = parsed.options.config !== void 0;
|
|
7472
|
+
const hasConfig = existsSync10(configPath);
|
|
7473
|
+
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
7474
|
+
await loadProjectConfig(configArgument);
|
|
7475
|
+
}
|
|
7476
|
+
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
7477
|
+
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
7478
|
+
const platformEnvironment = clean2(process10.env.ODLA_PLATFORM_URL);
|
|
7479
|
+
const platformValue = platformAudience(
|
|
7480
|
+
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
7481
|
+
);
|
|
7482
|
+
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
7483
|
+
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
7484
|
+
const appEnvironment = clean2(process10.env.ODLA_APP_ID);
|
|
7485
|
+
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
7486
|
+
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
7487
|
+
if (appValue) assertOperatorName(appValue, "app");
|
|
7488
|
+
if (options.requireApp && !appValue) {
|
|
7489
|
+
throw new Error(
|
|
7490
|
+
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
7491
|
+
);
|
|
7492
|
+
}
|
|
7493
|
+
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
7494
|
+
const envEnvironment = clean2(process10.env.ODLA_ENV);
|
|
7495
|
+
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
7496
|
+
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
7497
|
+
if (environmentValue) {
|
|
7498
|
+
assertOperatorName(environmentValue, "environment");
|
|
7499
|
+
}
|
|
7500
|
+
const rootDir = loaded?.rootDir ?? process10.cwd();
|
|
7501
|
+
const profileCredentials = operatorCredentialFiles(profile);
|
|
7502
|
+
const tokenFile = clean2(process10.env.ODLA_DEV_TOKEN_FILE) ? resolve11(process10.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
7503
|
+
const scopedTokenFile = clean2(process10.env.ODLA_ADMIN_TOKEN_FILE) ? resolve11(process10.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join10(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
7504
|
+
const cfg = loaded ? {
|
|
7505
|
+
...loaded,
|
|
7506
|
+
platformUrl: platformValue,
|
|
7507
|
+
app: appValue ? {
|
|
7508
|
+
id: appValue,
|
|
7509
|
+
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
7510
|
+
} : loaded.app,
|
|
7511
|
+
local: { ...loaded.local, tokenFile }
|
|
7512
|
+
} : {
|
|
7513
|
+
configPath,
|
|
7514
|
+
rootDir,
|
|
7515
|
+
platformUrl: platformValue,
|
|
7516
|
+
dbEndpoint: platformValue,
|
|
7517
|
+
app: {
|
|
7518
|
+
id: appValue ?? "operator",
|
|
7519
|
+
name: appValue ?? "Operator"
|
|
7520
|
+
},
|
|
7521
|
+
envs: environmentValue ? [environmentValue] : [],
|
|
7522
|
+
services: [],
|
|
7523
|
+
local: {
|
|
7524
|
+
tokenFile,
|
|
7525
|
+
credentialsFile: join10(rootDir, ".odla", "credentials.local.json"),
|
|
7526
|
+
devVarsFile: join10(rootDir, ".dev.vars"),
|
|
7527
|
+
gitignore: true
|
|
7528
|
+
}
|
|
7529
|
+
};
|
|
7530
|
+
return {
|
|
7531
|
+
cfg,
|
|
7532
|
+
profile: {
|
|
7533
|
+
name: profile.name,
|
|
7534
|
+
source: profile.source,
|
|
7535
|
+
file: profile.file
|
|
7536
|
+
},
|
|
7537
|
+
config: {
|
|
7538
|
+
path: configPath,
|
|
7539
|
+
status: loaded ? "loaded" : "absent",
|
|
7540
|
+
explicit: explicitConfig
|
|
7541
|
+
},
|
|
7542
|
+
platform: { value: platformValue, source: platformSource },
|
|
7543
|
+
app: { value: appValue, source: appSource },
|
|
7544
|
+
environment: {
|
|
7545
|
+
value: environmentValue,
|
|
7546
|
+
source: environmentSource
|
|
7547
|
+
},
|
|
7548
|
+
credentials: {
|
|
7549
|
+
developerTokenFile: tokenFile,
|
|
7550
|
+
scopedTokenFile
|
|
7551
|
+
}
|
|
7552
|
+
};
|
|
7553
|
+
}
|
|
7554
|
+
function clean2(value) {
|
|
7555
|
+
const normalized = value?.trim();
|
|
7556
|
+
return normalized || void 0;
|
|
7557
|
+
}
|
|
7558
|
+
|
|
7331
7559
|
// src/admin-command.ts
|
|
7332
|
-
|
|
7560
|
+
var CONTEXT_OPTIONS = ["platform", "config", "context", "token", "open", "email"];
|
|
7561
|
+
var JSON_OPTIONS = [...CONTEXT_OPTIONS, "json"];
|
|
7562
|
+
var SET_OPTIONS = [
|
|
7563
|
+
...JSON_OPTIONS,
|
|
7564
|
+
"provider",
|
|
7565
|
+
"model",
|
|
7566
|
+
"enabled",
|
|
7567
|
+
"max-input-bytes",
|
|
7568
|
+
"max-output-tokens",
|
|
7569
|
+
"max-calls-per-run",
|
|
7570
|
+
"discovery-provider",
|
|
7571
|
+
"discovery-model",
|
|
7572
|
+
"validation-provider",
|
|
7573
|
+
"validation-model"
|
|
7574
|
+
];
|
|
7575
|
+
async function adminCommand(parsed, deps = {}) {
|
|
7333
7576
|
const area = parsed.positionals[1];
|
|
7334
7577
|
const action = parsed.positionals[2];
|
|
7335
7578
|
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
@@ -7340,28 +7583,9 @@ async function adminCommand(parsed) {
|
|
|
7340
7583
|
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
7341
7584
|
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
7342
7585
|
}
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
"open",
|
|
7347
|
-
"email",
|
|
7348
|
-
"provider",
|
|
7349
|
-
"model",
|
|
7350
|
-
"enabled",
|
|
7351
|
-
"max-input-bytes",
|
|
7352
|
-
"max-output-tokens",
|
|
7353
|
-
"max-calls-per-run",
|
|
7354
|
-
"from-env",
|
|
7355
|
-
"stdin",
|
|
7356
|
-
"discovery-provider",
|
|
7357
|
-
"discovery-model",
|
|
7358
|
-
"validation-provider",
|
|
7359
|
-
"validation-model",
|
|
7360
|
-
"app-id",
|
|
7361
|
-
"env",
|
|
7362
|
-
"run-id",
|
|
7363
|
-
"limit"
|
|
7364
|
-
], credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
7586
|
+
const allowed = credentialSet ? [...CONTEXT_OPTIONS, "from-env", "stdin"] : action === "set" ? SET_OPTIONS : models ? [...JSON_OPTIONS, "provider"] : usage ? [...JSON_OPTIONS, "app-id", "env", "run-id", "limit"] : audit ? [...JSON_OPTIONS, "limit"] : JSON_OPTIONS;
|
|
7587
|
+
assertArgs(parsed, allowed, credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
7588
|
+
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
7365
7589
|
await adminAi({
|
|
7366
7590
|
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
7367
7591
|
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
@@ -7371,13 +7595,16 @@ async function adminCommand(parsed) {
|
|
|
7371
7595
|
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
7372
7596
|
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
7373
7597
|
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
7374
|
-
platform:
|
|
7598
|
+
platform: context.platform.value,
|
|
7599
|
+
token: stringOpt(parsed.options.token),
|
|
7600
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
7375
7601
|
email: stringOpt(parsed.options.email),
|
|
7376
7602
|
json: parsed.options.json === true,
|
|
7377
7603
|
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
7378
7604
|
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
7379
7605
|
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
7380
7606
|
stdin: parsed.options.stdin === true,
|
|
7607
|
+
readStdin: deps.readStdin,
|
|
7381
7608
|
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
7382
7609
|
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
7383
7610
|
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
@@ -7385,7 +7612,10 @@ async function adminCommand(parsed) {
|
|
|
7385
7612
|
appId: stringOpt(parsed.options["app-id"]),
|
|
7386
7613
|
env: stringOpt(parsed.options.env),
|
|
7387
7614
|
runId: stringOpt(parsed.options["run-id"]),
|
|
7388
|
-
limit: numberOpt(parsed.options.limit, "--limit")
|
|
7615
|
+
limit: numberOpt(parsed.options.limit, "--limit"),
|
|
7616
|
+
fetch: deps.fetch,
|
|
7617
|
+
stdout: deps.stdout,
|
|
7618
|
+
openApprovalUrl: deps.openUrl
|
|
7389
7619
|
});
|
|
7390
7620
|
}
|
|
7391
7621
|
|
|
@@ -7537,7 +7767,7 @@ async function appExport(options) {
|
|
|
7537
7767
|
}
|
|
7538
7768
|
|
|
7539
7769
|
// src/app-import.ts
|
|
7540
|
-
import { readFileSync as
|
|
7770
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
7541
7771
|
import {
|
|
7542
7772
|
buildImportOps,
|
|
7543
7773
|
importMutationId,
|
|
@@ -7561,7 +7791,7 @@ async function appImport(options) {
|
|
|
7561
7791
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
7562
7792
|
const doFetch = options.fetch ?? fetch;
|
|
7563
7793
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
7564
|
-
const text = options.file === "-" ? (options.readStdin ?? (() =>
|
|
7794
|
+
const text = options.file === "-" ? (options.readStdin ?? (() => readFileSync8(0, "utf8")))() : readFileSync8(options.file, "utf8");
|
|
7565
7795
|
const { format, sources } = parseImport(text, options.ns);
|
|
7566
7796
|
if (format === "namespace-map" && options.ns) {
|
|
7567
7797
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -8024,13 +8254,16 @@ function harnessOption(value, flag) {
|
|
|
8024
8254
|
|
|
8025
8255
|
// src/cli-project.ts
|
|
8026
8256
|
var SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
8027
|
-
function install(parsed, positionals) {
|
|
8257
|
+
function install(parsed, positionals, deps) {
|
|
8028
8258
|
assertArgs(parsed, SKILL_OPTS, positionals);
|
|
8029
8259
|
installSkill({
|
|
8030
8260
|
dir: stringOpt(parsed.options.dir),
|
|
8031
8261
|
global: parsed.options.global === true,
|
|
8032
8262
|
harnesses: harnessList(parsed),
|
|
8033
|
-
force: parsed.options.force === true
|
|
8263
|
+
force: parsed.options.force === true,
|
|
8264
|
+
homeDir: deps.homeDir,
|
|
8265
|
+
codexHomeDir: deps.codexHomeDir,
|
|
8266
|
+
stdout: deps.stdout
|
|
8034
8267
|
});
|
|
8035
8268
|
}
|
|
8036
8269
|
async function secretsCommand(parsed, deps) {
|
|
@@ -8046,6 +8279,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
8046
8279
|
token: stringOpt(parsed.options.token),
|
|
8047
8280
|
email: stringOpt(parsed.options.email),
|
|
8048
8281
|
yes: parsed.options.yes === true,
|
|
8282
|
+
readStdin: deps.readStdin,
|
|
8049
8283
|
fetch: deps.fetch,
|
|
8050
8284
|
stdout: deps.stdout
|
|
8051
8285
|
};
|
|
@@ -8104,8 +8338,8 @@ async function projectCommand(command, parsed, deps) {
|
|
|
8104
8338
|
return true;
|
|
8105
8339
|
}
|
|
8106
8340
|
if (command === "setup") {
|
|
8107
|
-
install(parsed, 1);
|
|
8108
|
-
console.log(
|
|
8341
|
+
install(parsed, 1, deps);
|
|
8342
|
+
(deps.stdout ?? console).log(
|
|
8109
8343
|
"\nOffline runbooks installed. Open this repo in your coding agent and ask it to set up odla.\nIts native adapter finds the local `odla` skill, chooses build or migration, and drives the CLI."
|
|
8110
8344
|
);
|
|
8111
8345
|
return true;
|
|
@@ -8113,7 +8347,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
8113
8347
|
if (command === "skill") {
|
|
8114
8348
|
const sub = parsed.positionals[1];
|
|
8115
8349
|
if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
|
|
8116
|
-
install(parsed, 2);
|
|
8350
|
+
install(parsed, 2, deps);
|
|
8117
8351
|
return true;
|
|
8118
8352
|
}
|
|
8119
8353
|
if (command === "secrets") {
|
|
@@ -8166,246 +8400,19 @@ async function codeCommand(parsed, dependencies) {
|
|
|
8166
8400
|
}
|
|
8167
8401
|
|
|
8168
8402
|
// src/operator-credentials.ts
|
|
8169
|
-
import
|
|
8403
|
+
import process11 from "process";
|
|
8170
8404
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
8171
8405
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
8172
8406
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
8173
|
-
const source =
|
|
8407
|
+
const source = clean3(
|
|
8174
8408
|
stringOpt(parsed.options.token)
|
|
8175
|
-
) ? "flag" :
|
|
8409
|
+
) ? "flag" : clean3(process11.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
8176
8410
|
return {
|
|
8177
8411
|
source,
|
|
8178
8412
|
cacheFile: context.cfg.local.tokenFile,
|
|
8179
8413
|
cacheStatus
|
|
8180
8414
|
};
|
|
8181
8415
|
}
|
|
8182
|
-
function clean(value) {
|
|
8183
|
-
const normalized = value?.trim();
|
|
8184
|
-
return normalized || void 0;
|
|
8185
|
-
}
|
|
8186
|
-
|
|
8187
|
-
// src/operator-context.ts
|
|
8188
|
-
import { existsSync as existsSync10 } from "fs";
|
|
8189
|
-
import { join as join10, resolve as resolve11 } from "path";
|
|
8190
|
-
import process11 from "process";
|
|
8191
|
-
|
|
8192
|
-
// src/operator-profiles.ts
|
|
8193
|
-
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
8194
|
-
import { homedir as homedir2 } from "os";
|
|
8195
|
-
import { dirname as dirname7, join as join9, resolve as resolve10 } from "path";
|
|
8196
|
-
import process10 from "process";
|
|
8197
|
-
function operatorProfileFile() {
|
|
8198
|
-
return resolve10(
|
|
8199
|
-
clean2(process10.env.ODLA_CONTEXT_FILE) ?? join9(homedir2(), ".odla", "contexts.json")
|
|
8200
|
-
);
|
|
8201
|
-
}
|
|
8202
|
-
function resolveOperatorProfile(parsed) {
|
|
8203
|
-
const fromFlag = clean2(stringOpt(parsed.options.context));
|
|
8204
|
-
const fromEnvironment = clean2(process10.env.ODLA_CONTEXT);
|
|
8205
|
-
const name = fromFlag ?? fromEnvironment ?? null;
|
|
8206
|
-
const file = operatorProfileFile();
|
|
8207
|
-
if (!name) {
|
|
8208
|
-
return { name: null, source: "unresolved", file, value: null };
|
|
8209
|
-
}
|
|
8210
|
-
assertOperatorName(name, "context");
|
|
8211
|
-
const profiles = readOperatorProfiles(file);
|
|
8212
|
-
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
8213
|
-
if (!value) {
|
|
8214
|
-
throw new Error(
|
|
8215
|
-
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
8216
|
-
);
|
|
8217
|
-
}
|
|
8218
|
-
return {
|
|
8219
|
-
name,
|
|
8220
|
-
source: fromFlag ? "flag" : "environment",
|
|
8221
|
-
file,
|
|
8222
|
-
value
|
|
8223
|
-
};
|
|
8224
|
-
}
|
|
8225
|
-
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
8226
|
-
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
8227
|
-
}
|
|
8228
|
-
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
8229
|
-
assertOperatorName(name, "context");
|
|
8230
|
-
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
8231
|
-
const profiles = readOperatorProfiles(file);
|
|
8232
|
-
profiles[name] = normalized;
|
|
8233
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
8234
|
-
}
|
|
8235
|
-
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
8236
|
-
assertOperatorName(name, "context");
|
|
8237
|
-
const profiles = readOperatorProfiles(file);
|
|
8238
|
-
if (!Object.hasOwn(profiles, name)) return false;
|
|
8239
|
-
delete profiles[name];
|
|
8240
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
8241
|
-
return true;
|
|
8242
|
-
}
|
|
8243
|
-
function operatorCredentialFiles(selection) {
|
|
8244
|
-
const base = selection.name ? join9(dirname7(selection.file), "profiles", selection.name) : join9(homedir2(), ".odla");
|
|
8245
|
-
return {
|
|
8246
|
-
developer: join9(base, "dev-token.json"),
|
|
8247
|
-
scoped: join9(base, "admin-token.local.json")
|
|
8248
|
-
};
|
|
8249
|
-
}
|
|
8250
|
-
function assertOperatorName(value, label) {
|
|
8251
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
8252
|
-
throw new Error(
|
|
8253
|
-
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
8254
|
-
);
|
|
8255
|
-
}
|
|
8256
|
-
}
|
|
8257
|
-
function readOperatorProfiles(file) {
|
|
8258
|
-
if (!existsSync9(file)) return emptyProfiles();
|
|
8259
|
-
let raw;
|
|
8260
|
-
try {
|
|
8261
|
-
raw = JSON.parse(readFileSync8(file, "utf8"));
|
|
8262
|
-
} catch {
|
|
8263
|
-
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
8264
|
-
}
|
|
8265
|
-
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
8266
|
-
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
8267
|
-
}
|
|
8268
|
-
const unknown = Object.keys(raw).filter(
|
|
8269
|
-
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
8270
|
-
);
|
|
8271
|
-
if (unknown.length > 0) {
|
|
8272
|
-
throw new Error(
|
|
8273
|
-
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
8274
|
-
);
|
|
8275
|
-
}
|
|
8276
|
-
const profiles = emptyProfiles();
|
|
8277
|
-
for (const [name, value] of Object.entries(
|
|
8278
|
-
raw.profiles
|
|
8279
|
-
)) {
|
|
8280
|
-
assertOperatorName(name, "context");
|
|
8281
|
-
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
8282
|
-
}
|
|
8283
|
-
return profiles;
|
|
8284
|
-
}
|
|
8285
|
-
function emptyProfiles() {
|
|
8286
|
-
return /* @__PURE__ */ Object.create(null);
|
|
8287
|
-
}
|
|
8288
|
-
function validateProfile(value, label) {
|
|
8289
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8290
|
-
throw new Error(`${label} has an invalid shape`);
|
|
8291
|
-
}
|
|
8292
|
-
const unknown = Object.keys(value).filter(
|
|
8293
|
-
(key) => !["platform", "app", "environment"].includes(key)
|
|
8294
|
-
);
|
|
8295
|
-
if (unknown.length > 0) {
|
|
8296
|
-
throw new Error(
|
|
8297
|
-
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
8298
|
-
);
|
|
8299
|
-
}
|
|
8300
|
-
const candidate = value;
|
|
8301
|
-
if (typeof candidate.platform !== "string") {
|
|
8302
|
-
throw new Error(`${label} needs an absolute platform URL`);
|
|
8303
|
-
}
|
|
8304
|
-
const platform = platformAudience(candidate.platform);
|
|
8305
|
-
const app = clean2(candidate.app);
|
|
8306
|
-
const environment2 = clean2(candidate.environment);
|
|
8307
|
-
if (app) assertOperatorName(app, "app");
|
|
8308
|
-
if (environment2) assertOperatorName(environment2, "environment");
|
|
8309
|
-
return {
|
|
8310
|
-
platform,
|
|
8311
|
-
...app ? { app } : {},
|
|
8312
|
-
...environment2 ? { environment: environment2 } : {}
|
|
8313
|
-
};
|
|
8314
|
-
}
|
|
8315
|
-
function clean2(value) {
|
|
8316
|
-
const normalized = value?.trim();
|
|
8317
|
-
return normalized || void 0;
|
|
8318
|
-
}
|
|
8319
|
-
|
|
8320
|
-
// src/operator-context.ts
|
|
8321
|
-
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
8322
|
-
async function resolveOperatorContext(parsed, options = {}) {
|
|
8323
|
-
const profile = resolveOperatorProfile(parsed);
|
|
8324
|
-
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
8325
|
-
const configPath = resolve11(configArgument);
|
|
8326
|
-
const explicitConfig = parsed.options.config !== void 0;
|
|
8327
|
-
const hasConfig = existsSync10(configPath);
|
|
8328
|
-
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
8329
|
-
await loadProjectConfig(configArgument);
|
|
8330
|
-
}
|
|
8331
|
-
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
8332
|
-
const platformFlag = clean3(stringOpt(parsed.options.platform));
|
|
8333
|
-
const platformEnvironment = clean3(process11.env.ODLA_PLATFORM_URL);
|
|
8334
|
-
const platformValue = platformAudience(
|
|
8335
|
-
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
8336
|
-
);
|
|
8337
|
-
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
8338
|
-
const appFlag = clean3(stringOpt(parsed.options.app));
|
|
8339
|
-
const appEnvironment = clean3(process11.env.ODLA_APP_ID);
|
|
8340
|
-
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
8341
|
-
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
8342
|
-
if (appValue) assertOperatorName(appValue, "app");
|
|
8343
|
-
if (options.requireApp && !appValue) {
|
|
8344
|
-
throw new Error(
|
|
8345
|
-
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
8346
|
-
);
|
|
8347
|
-
}
|
|
8348
|
-
const envFlag = clean3(stringOpt(parsed.options.env));
|
|
8349
|
-
const envEnvironment = clean3(process11.env.ODLA_ENV);
|
|
8350
|
-
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
8351
|
-
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
8352
|
-
if (environmentValue) {
|
|
8353
|
-
assertOperatorName(environmentValue, "environment");
|
|
8354
|
-
}
|
|
8355
|
-
const rootDir = loaded?.rootDir ?? process11.cwd();
|
|
8356
|
-
const profileCredentials = operatorCredentialFiles(profile);
|
|
8357
|
-
const tokenFile = clean3(process11.env.ODLA_DEV_TOKEN_FILE) ? resolve11(process11.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
8358
|
-
const scopedTokenFile = clean3(process11.env.ODLA_ADMIN_TOKEN_FILE) ? resolve11(process11.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join10(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
8359
|
-
const cfg = loaded ? {
|
|
8360
|
-
...loaded,
|
|
8361
|
-
platformUrl: platformValue,
|
|
8362
|
-
app: appValue ? {
|
|
8363
|
-
id: appValue,
|
|
8364
|
-
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
8365
|
-
} : loaded.app,
|
|
8366
|
-
local: { ...loaded.local, tokenFile }
|
|
8367
|
-
} : {
|
|
8368
|
-
configPath,
|
|
8369
|
-
rootDir,
|
|
8370
|
-
platformUrl: platformValue,
|
|
8371
|
-
dbEndpoint: platformValue,
|
|
8372
|
-
app: {
|
|
8373
|
-
id: appValue ?? "operator",
|
|
8374
|
-
name: appValue ?? "Operator"
|
|
8375
|
-
},
|
|
8376
|
-
envs: environmentValue ? [environmentValue] : [],
|
|
8377
|
-
services: [],
|
|
8378
|
-
local: {
|
|
8379
|
-
tokenFile,
|
|
8380
|
-
credentialsFile: join10(rootDir, ".odla", "credentials.local.json"),
|
|
8381
|
-
devVarsFile: join10(rootDir, ".dev.vars"),
|
|
8382
|
-
gitignore: true
|
|
8383
|
-
}
|
|
8384
|
-
};
|
|
8385
|
-
return {
|
|
8386
|
-
cfg,
|
|
8387
|
-
profile: {
|
|
8388
|
-
name: profile.name,
|
|
8389
|
-
source: profile.source,
|
|
8390
|
-
file: profile.file
|
|
8391
|
-
},
|
|
8392
|
-
config: {
|
|
8393
|
-
path: configPath,
|
|
8394
|
-
status: loaded ? "loaded" : "absent",
|
|
8395
|
-
explicit: explicitConfig
|
|
8396
|
-
},
|
|
8397
|
-
platform: { value: platformValue, source: platformSource },
|
|
8398
|
-
app: { value: appValue, source: appSource },
|
|
8399
|
-
environment: {
|
|
8400
|
-
value: environmentValue,
|
|
8401
|
-
source: environmentSource
|
|
8402
|
-
},
|
|
8403
|
-
credentials: {
|
|
8404
|
-
developerTokenFile: tokenFile,
|
|
8405
|
-
scopedTokenFile
|
|
8406
|
-
}
|
|
8407
|
-
};
|
|
8408
|
-
}
|
|
8409
8416
|
function clean3(value) {
|
|
8410
8417
|
const normalized = value?.trim();
|
|
8411
8418
|
return normalized || void 0;
|
|
@@ -11205,7 +11212,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
11205
11212
|
return;
|
|
11206
11213
|
}
|
|
11207
11214
|
if (command === "admin") {
|
|
11208
|
-
await adminCommand(parsed);
|
|
11215
|
+
await adminCommand(parsed, runtime);
|
|
11209
11216
|
return;
|
|
11210
11217
|
}
|
|
11211
11218
|
if (command === "agent") {
|
|
@@ -11351,4 +11358,4 @@ export {
|
|
|
11351
11358
|
exitCodeFor,
|
|
11352
11359
|
runCli
|
|
11353
11360
|
};
|
|
11354
|
-
//# sourceMappingURL=chunk-
|
|
11361
|
+
//# sourceMappingURL=chunk-S3EJ66VG.js.map
|