@odla-ai/cli 0.25.17 → 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 +463 -431
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-5MRY3V2K.js → chunk-S3EJ66VG.js} +351 -319
- package/dist/chunk-S3EJ66VG.js.map +1 -0
- package/dist/index.cjs +463 -431
- 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-5MRY3V2K.js.map +0 -1
|
@@ -5980,11 +5980,21 @@ Usage:
|
|
|
5980
5980
|
odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
5981
5981
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
5982
5982
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
5983
|
-
odla-ai pm
|
|
5984
|
-
odla-ai pm
|
|
5983
|
+
odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
5984
|
+
odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
5985
|
+
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
5986
|
+
odla-ai pm bug list [--app <id>] [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
5987
|
+
odla-ai pm goal add --app <id> --title <t> [--status <s>] [--proof <text>] [--target <pct>] [--mutation-id <id>] [--json]
|
|
5988
|
+
odla-ai pm task add --app <id> --title <t> [--column <c>] [--goal <id>] [--assignee <id>] [--description <text>|--body <text>] [--due <epoch-ms>] [--mutation-id <id>] [--json]
|
|
5989
|
+
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
5990
|
+
odla-ai pm bug add --app <id> --title <t> (--description <text>|--body <text>) [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--mutation-id <id>] [--json]
|
|
5985
5991
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
5986
|
-
odla-ai pm
|
|
5987
|
-
odla-ai pm
|
|
5992
|
+
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
5993
|
+
odla-ai pm task set <id> [--title <t>|--column <c>|--rank <n>|--goal <id>|--no-goal|--assignee <id>|--no-assignee|--description <text>|--body <text>|--due <epoch-ms>|--no-due] [--mutation-id <id>] [--json]
|
|
5994
|
+
odla-ai pm decision set <id> [--title <t>|--status <s>|--body <text>] [--mutation-id <id>] [--json]
|
|
5995
|
+
odla-ai pm bug set <id> [--title <t>|--status <s>|--severity <s>|--goal <id>|--no-goal|--assignee <id>|--no-assignee|--decision <id>|--no-decision|--description <text>|--body <text>] [--mutation-id <id>] [--json]
|
|
5996
|
+
odla-ai pm <goal|task|decision> done <id> [--mutation-id <id>]
|
|
5997
|
+
odla-ai pm bug done <id> [--decision <accepted-decision-id>] [--mutation-id <id>]
|
|
5988
5998
|
odla-ai pm <goal|task|decision|bug> comment <id> --body "..." [--mutation-id <id>]
|
|
5989
5999
|
odla-ai pm <goal|task|decision|bug> comments <id> [--json]
|
|
5990
6000
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
@@ -6023,15 +6033,16 @@ Usage:
|
|
|
6023
6033
|
odla-ai runbook rm <slug> [--app <id>]
|
|
6024
6034
|
odla-ai capabilities [--json]
|
|
6025
6035
|
odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
|
|
6026
|
-
odla-ai admin ai show [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6027
|
-
odla-ai admin ai models [--provider <id>] [--json]
|
|
6028
|
-
odla-ai admin ai set <purpose> [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
odla-ai admin ai
|
|
6033
|
-
odla-ai admin ai
|
|
6034
|
-
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]
|
|
6035
6046
|
odla-ai security github connect [--repo owner/name] [--env dev] [--email <odla-account>] [--no-open]
|
|
6036
6047
|
odla-ai security github disconnect --source <id> [--env dev] [--yes]
|
|
6037
6048
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -7274,8 +7285,8 @@ function parseArgv(argv) {
|
|
|
7274
7285
|
}
|
|
7275
7286
|
return { positionals, options };
|
|
7276
7287
|
}
|
|
7277
|
-
function assertArgs(parsed,
|
|
7278
|
-
const allowed = new Set(
|
|
7288
|
+
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
7289
|
+
const allowed = new Set(allowedOptions2);
|
|
7279
7290
|
for (const name of Object.keys(parsed.options)) {
|
|
7280
7291
|
if (!allowed.has(name)) throw new Error(`unknown option "--${name}"; run "odla-ai help" for supported options`);
|
|
7281
7292
|
}
|
|
@@ -7318,8 +7329,250 @@ function addOption(options, name, value) {
|
|
|
7318
7329
|
else options[name] = [String(current), String(value)];
|
|
7319
7330
|
}
|
|
7320
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
|
+
|
|
7321
7559
|
// src/admin-command.ts
|
|
7322
|
-
|
|
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 = {}) {
|
|
7323
7576
|
const area = parsed.positionals[1];
|
|
7324
7577
|
const action = parsed.positionals[2];
|
|
7325
7578
|
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
@@ -7330,28 +7583,9 @@ async function adminCommand(parsed) {
|
|
|
7330
7583
|
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
7331
7584
|
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
7332
7585
|
}
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
"open",
|
|
7337
|
-
"email",
|
|
7338
|
-
"provider",
|
|
7339
|
-
"model",
|
|
7340
|
-
"enabled",
|
|
7341
|
-
"max-input-bytes",
|
|
7342
|
-
"max-output-tokens",
|
|
7343
|
-
"max-calls-per-run",
|
|
7344
|
-
"from-env",
|
|
7345
|
-
"stdin",
|
|
7346
|
-
"discovery-provider",
|
|
7347
|
-
"discovery-model",
|
|
7348
|
-
"validation-provider",
|
|
7349
|
-
"validation-model",
|
|
7350
|
-
"app-id",
|
|
7351
|
-
"env",
|
|
7352
|
-
"run-id",
|
|
7353
|
-
"limit"
|
|
7354
|
-
], 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 });
|
|
7355
7589
|
await adminAi({
|
|
7356
7590
|
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
7357
7591
|
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
@@ -7361,13 +7595,16 @@ async function adminCommand(parsed) {
|
|
|
7361
7595
|
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
7362
7596
|
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
7363
7597
|
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
7364
|
-
platform:
|
|
7598
|
+
platform: context.platform.value,
|
|
7599
|
+
token: stringOpt(parsed.options.token),
|
|
7600
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
7365
7601
|
email: stringOpt(parsed.options.email),
|
|
7366
7602
|
json: parsed.options.json === true,
|
|
7367
7603
|
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
7368
7604
|
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
7369
7605
|
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
7370
7606
|
stdin: parsed.options.stdin === true,
|
|
7607
|
+
readStdin: deps.readStdin,
|
|
7371
7608
|
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
7372
7609
|
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
7373
7610
|
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
@@ -7375,7 +7612,10 @@ async function adminCommand(parsed) {
|
|
|
7375
7612
|
appId: stringOpt(parsed.options["app-id"]),
|
|
7376
7613
|
env: stringOpt(parsed.options.env),
|
|
7377
7614
|
runId: stringOpt(parsed.options["run-id"]),
|
|
7378
|
-
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
|
|
7379
7619
|
});
|
|
7380
7620
|
}
|
|
7381
7621
|
|
|
@@ -7527,7 +7767,7 @@ async function appExport(options) {
|
|
|
7527
7767
|
}
|
|
7528
7768
|
|
|
7529
7769
|
// src/app-import.ts
|
|
7530
|
-
import { readFileSync as
|
|
7770
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
7531
7771
|
import {
|
|
7532
7772
|
buildImportOps,
|
|
7533
7773
|
importMutationId,
|
|
@@ -7551,7 +7791,7 @@ async function appImport(options) {
|
|
|
7551
7791
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
7552
7792
|
const doFetch = options.fetch ?? fetch;
|
|
7553
7793
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
7554
|
-
const text = options.file === "-" ? (options.readStdin ?? (() =>
|
|
7794
|
+
const text = options.file === "-" ? (options.readStdin ?? (() => readFileSync8(0, "utf8")))() : readFileSync8(options.file, "utf8");
|
|
7555
7795
|
const { format, sources } = parseImport(text, options.ns);
|
|
7556
7796
|
if (format === "namespace-map" && options.ns) {
|
|
7557
7797
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -8014,13 +8254,16 @@ function harnessOption(value, flag) {
|
|
|
8014
8254
|
|
|
8015
8255
|
// src/cli-project.ts
|
|
8016
8256
|
var SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
8017
|
-
function install(parsed, positionals) {
|
|
8257
|
+
function install(parsed, positionals, deps) {
|
|
8018
8258
|
assertArgs(parsed, SKILL_OPTS, positionals);
|
|
8019
8259
|
installSkill({
|
|
8020
8260
|
dir: stringOpt(parsed.options.dir),
|
|
8021
8261
|
global: parsed.options.global === true,
|
|
8022
8262
|
harnesses: harnessList(parsed),
|
|
8023
|
-
force: parsed.options.force === true
|
|
8263
|
+
force: parsed.options.force === true,
|
|
8264
|
+
homeDir: deps.homeDir,
|
|
8265
|
+
codexHomeDir: deps.codexHomeDir,
|
|
8266
|
+
stdout: deps.stdout
|
|
8024
8267
|
});
|
|
8025
8268
|
}
|
|
8026
8269
|
async function secretsCommand(parsed, deps) {
|
|
@@ -8036,6 +8279,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
8036
8279
|
token: stringOpt(parsed.options.token),
|
|
8037
8280
|
email: stringOpt(parsed.options.email),
|
|
8038
8281
|
yes: parsed.options.yes === true,
|
|
8282
|
+
readStdin: deps.readStdin,
|
|
8039
8283
|
fetch: deps.fetch,
|
|
8040
8284
|
stdout: deps.stdout
|
|
8041
8285
|
};
|
|
@@ -8094,8 +8338,8 @@ async function projectCommand(command, parsed, deps) {
|
|
|
8094
8338
|
return true;
|
|
8095
8339
|
}
|
|
8096
8340
|
if (command === "setup") {
|
|
8097
|
-
install(parsed, 1);
|
|
8098
|
-
console.log(
|
|
8341
|
+
install(parsed, 1, deps);
|
|
8342
|
+
(deps.stdout ?? console).log(
|
|
8099
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."
|
|
8100
8344
|
);
|
|
8101
8345
|
return true;
|
|
@@ -8103,7 +8347,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
8103
8347
|
if (command === "skill") {
|
|
8104
8348
|
const sub = parsed.positionals[1];
|
|
8105
8349
|
if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
|
|
8106
|
-
install(parsed, 2);
|
|
8350
|
+
install(parsed, 2, deps);
|
|
8107
8351
|
return true;
|
|
8108
8352
|
}
|
|
8109
8353
|
if (command === "secrets") {
|
|
@@ -8156,246 +8400,19 @@ async function codeCommand(parsed, dependencies) {
|
|
|
8156
8400
|
}
|
|
8157
8401
|
|
|
8158
8402
|
// src/operator-credentials.ts
|
|
8159
|
-
import
|
|
8403
|
+
import process11 from "process";
|
|
8160
8404
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
8161
8405
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
8162
8406
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
8163
|
-
const source =
|
|
8407
|
+
const source = clean3(
|
|
8164
8408
|
stringOpt(parsed.options.token)
|
|
8165
|
-
) ? "flag" :
|
|
8409
|
+
) ? "flag" : clean3(process11.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
8166
8410
|
return {
|
|
8167
8411
|
source,
|
|
8168
8412
|
cacheFile: context.cfg.local.tokenFile,
|
|
8169
8413
|
cacheStatus
|
|
8170
8414
|
};
|
|
8171
8415
|
}
|
|
8172
|
-
function clean(value) {
|
|
8173
|
-
const normalized = value?.trim();
|
|
8174
|
-
return normalized || void 0;
|
|
8175
|
-
}
|
|
8176
|
-
|
|
8177
|
-
// src/operator-context.ts
|
|
8178
|
-
import { existsSync as existsSync10 } from "fs";
|
|
8179
|
-
import { join as join10, resolve as resolve11 } from "path";
|
|
8180
|
-
import process11 from "process";
|
|
8181
|
-
|
|
8182
|
-
// src/operator-profiles.ts
|
|
8183
|
-
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
8184
|
-
import { homedir as homedir2 } from "os";
|
|
8185
|
-
import { dirname as dirname7, join as join9, resolve as resolve10 } from "path";
|
|
8186
|
-
import process10 from "process";
|
|
8187
|
-
function operatorProfileFile() {
|
|
8188
|
-
return resolve10(
|
|
8189
|
-
clean2(process10.env.ODLA_CONTEXT_FILE) ?? join9(homedir2(), ".odla", "contexts.json")
|
|
8190
|
-
);
|
|
8191
|
-
}
|
|
8192
|
-
function resolveOperatorProfile(parsed) {
|
|
8193
|
-
const fromFlag = clean2(stringOpt(parsed.options.context));
|
|
8194
|
-
const fromEnvironment = clean2(process10.env.ODLA_CONTEXT);
|
|
8195
|
-
const name = fromFlag ?? fromEnvironment ?? null;
|
|
8196
|
-
const file = operatorProfileFile();
|
|
8197
|
-
if (!name) {
|
|
8198
|
-
return { name: null, source: "unresolved", file, value: null };
|
|
8199
|
-
}
|
|
8200
|
-
assertOperatorName(name, "context");
|
|
8201
|
-
const profiles = readOperatorProfiles(file);
|
|
8202
|
-
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
8203
|
-
if (!value) {
|
|
8204
|
-
throw new Error(
|
|
8205
|
-
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
8206
|
-
);
|
|
8207
|
-
}
|
|
8208
|
-
return {
|
|
8209
|
-
name,
|
|
8210
|
-
source: fromFlag ? "flag" : "environment",
|
|
8211
|
-
file,
|
|
8212
|
-
value
|
|
8213
|
-
};
|
|
8214
|
-
}
|
|
8215
|
-
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
8216
|
-
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
8217
|
-
}
|
|
8218
|
-
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
8219
|
-
assertOperatorName(name, "context");
|
|
8220
|
-
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
8221
|
-
const profiles = readOperatorProfiles(file);
|
|
8222
|
-
profiles[name] = normalized;
|
|
8223
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
8224
|
-
}
|
|
8225
|
-
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
8226
|
-
assertOperatorName(name, "context");
|
|
8227
|
-
const profiles = readOperatorProfiles(file);
|
|
8228
|
-
if (!Object.hasOwn(profiles, name)) return false;
|
|
8229
|
-
delete profiles[name];
|
|
8230
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
8231
|
-
return true;
|
|
8232
|
-
}
|
|
8233
|
-
function operatorCredentialFiles(selection) {
|
|
8234
|
-
const base = selection.name ? join9(dirname7(selection.file), "profiles", selection.name) : join9(homedir2(), ".odla");
|
|
8235
|
-
return {
|
|
8236
|
-
developer: join9(base, "dev-token.json"),
|
|
8237
|
-
scoped: join9(base, "admin-token.local.json")
|
|
8238
|
-
};
|
|
8239
|
-
}
|
|
8240
|
-
function assertOperatorName(value, label) {
|
|
8241
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
8242
|
-
throw new Error(
|
|
8243
|
-
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
8244
|
-
);
|
|
8245
|
-
}
|
|
8246
|
-
}
|
|
8247
|
-
function readOperatorProfiles(file) {
|
|
8248
|
-
if (!existsSync9(file)) return emptyProfiles();
|
|
8249
|
-
let raw;
|
|
8250
|
-
try {
|
|
8251
|
-
raw = JSON.parse(readFileSync8(file, "utf8"));
|
|
8252
|
-
} catch {
|
|
8253
|
-
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
8254
|
-
}
|
|
8255
|
-
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
8256
|
-
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
8257
|
-
}
|
|
8258
|
-
const unknown = Object.keys(raw).filter(
|
|
8259
|
-
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
8260
|
-
);
|
|
8261
|
-
if (unknown.length > 0) {
|
|
8262
|
-
throw new Error(
|
|
8263
|
-
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
8264
|
-
);
|
|
8265
|
-
}
|
|
8266
|
-
const profiles = emptyProfiles();
|
|
8267
|
-
for (const [name, value] of Object.entries(
|
|
8268
|
-
raw.profiles
|
|
8269
|
-
)) {
|
|
8270
|
-
assertOperatorName(name, "context");
|
|
8271
|
-
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
8272
|
-
}
|
|
8273
|
-
return profiles;
|
|
8274
|
-
}
|
|
8275
|
-
function emptyProfiles() {
|
|
8276
|
-
return /* @__PURE__ */ Object.create(null);
|
|
8277
|
-
}
|
|
8278
|
-
function validateProfile(value, label) {
|
|
8279
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8280
|
-
throw new Error(`${label} has an invalid shape`);
|
|
8281
|
-
}
|
|
8282
|
-
const unknown = Object.keys(value).filter(
|
|
8283
|
-
(key) => !["platform", "app", "environment"].includes(key)
|
|
8284
|
-
);
|
|
8285
|
-
if (unknown.length > 0) {
|
|
8286
|
-
throw new Error(
|
|
8287
|
-
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
8288
|
-
);
|
|
8289
|
-
}
|
|
8290
|
-
const candidate = value;
|
|
8291
|
-
if (typeof candidate.platform !== "string") {
|
|
8292
|
-
throw new Error(`${label} needs an absolute platform URL`);
|
|
8293
|
-
}
|
|
8294
|
-
const platform = platformAudience(candidate.platform);
|
|
8295
|
-
const app = clean2(candidate.app);
|
|
8296
|
-
const environment2 = clean2(candidate.environment);
|
|
8297
|
-
if (app) assertOperatorName(app, "app");
|
|
8298
|
-
if (environment2) assertOperatorName(environment2, "environment");
|
|
8299
|
-
return {
|
|
8300
|
-
platform,
|
|
8301
|
-
...app ? { app } : {},
|
|
8302
|
-
...environment2 ? { environment: environment2 } : {}
|
|
8303
|
-
};
|
|
8304
|
-
}
|
|
8305
|
-
function clean2(value) {
|
|
8306
|
-
const normalized = value?.trim();
|
|
8307
|
-
return normalized || void 0;
|
|
8308
|
-
}
|
|
8309
|
-
|
|
8310
|
-
// src/operator-context.ts
|
|
8311
|
-
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
8312
|
-
async function resolveOperatorContext(parsed, options = {}) {
|
|
8313
|
-
const profile = resolveOperatorProfile(parsed);
|
|
8314
|
-
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
8315
|
-
const configPath = resolve11(configArgument);
|
|
8316
|
-
const explicitConfig = parsed.options.config !== void 0;
|
|
8317
|
-
const hasConfig = existsSync10(configPath);
|
|
8318
|
-
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
8319
|
-
await loadProjectConfig(configArgument);
|
|
8320
|
-
}
|
|
8321
|
-
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
8322
|
-
const platformFlag = clean3(stringOpt(parsed.options.platform));
|
|
8323
|
-
const platformEnvironment = clean3(process11.env.ODLA_PLATFORM_URL);
|
|
8324
|
-
const platformValue = platformAudience(
|
|
8325
|
-
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
8326
|
-
);
|
|
8327
|
-
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
8328
|
-
const appFlag = clean3(stringOpt(parsed.options.app));
|
|
8329
|
-
const appEnvironment = clean3(process11.env.ODLA_APP_ID);
|
|
8330
|
-
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
8331
|
-
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
8332
|
-
if (appValue) assertOperatorName(appValue, "app");
|
|
8333
|
-
if (options.requireApp && !appValue) {
|
|
8334
|
-
throw new Error(
|
|
8335
|
-
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
8336
|
-
);
|
|
8337
|
-
}
|
|
8338
|
-
const envFlag = clean3(stringOpt(parsed.options.env));
|
|
8339
|
-
const envEnvironment = clean3(process11.env.ODLA_ENV);
|
|
8340
|
-
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
8341
|
-
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
8342
|
-
if (environmentValue) {
|
|
8343
|
-
assertOperatorName(environmentValue, "environment");
|
|
8344
|
-
}
|
|
8345
|
-
const rootDir = loaded?.rootDir ?? process11.cwd();
|
|
8346
|
-
const profileCredentials = operatorCredentialFiles(profile);
|
|
8347
|
-
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;
|
|
8348
|
-
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;
|
|
8349
|
-
const cfg = loaded ? {
|
|
8350
|
-
...loaded,
|
|
8351
|
-
platformUrl: platformValue,
|
|
8352
|
-
app: appValue ? {
|
|
8353
|
-
id: appValue,
|
|
8354
|
-
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
8355
|
-
} : loaded.app,
|
|
8356
|
-
local: { ...loaded.local, tokenFile }
|
|
8357
|
-
} : {
|
|
8358
|
-
configPath,
|
|
8359
|
-
rootDir,
|
|
8360
|
-
platformUrl: platformValue,
|
|
8361
|
-
dbEndpoint: platformValue,
|
|
8362
|
-
app: {
|
|
8363
|
-
id: appValue ?? "operator",
|
|
8364
|
-
name: appValue ?? "Operator"
|
|
8365
|
-
},
|
|
8366
|
-
envs: environmentValue ? [environmentValue] : [],
|
|
8367
|
-
services: [],
|
|
8368
|
-
local: {
|
|
8369
|
-
tokenFile,
|
|
8370
|
-
credentialsFile: join10(rootDir, ".odla", "credentials.local.json"),
|
|
8371
|
-
devVarsFile: join10(rootDir, ".dev.vars"),
|
|
8372
|
-
gitignore: true
|
|
8373
|
-
}
|
|
8374
|
-
};
|
|
8375
|
-
return {
|
|
8376
|
-
cfg,
|
|
8377
|
-
profile: {
|
|
8378
|
-
name: profile.name,
|
|
8379
|
-
source: profile.source,
|
|
8380
|
-
file: profile.file
|
|
8381
|
-
},
|
|
8382
|
-
config: {
|
|
8383
|
-
path: configPath,
|
|
8384
|
-
status: loaded ? "loaded" : "absent",
|
|
8385
|
-
explicit: explicitConfig
|
|
8386
|
-
},
|
|
8387
|
-
platform: { value: platformValue, source: platformSource },
|
|
8388
|
-
app: { value: appValue, source: appSource },
|
|
8389
|
-
environment: {
|
|
8390
|
-
value: environmentValue,
|
|
8391
|
-
source: environmentSource
|
|
8392
|
-
},
|
|
8393
|
-
credentials: {
|
|
8394
|
-
developerTokenFile: tokenFile,
|
|
8395
|
-
scopedTokenFile
|
|
8396
|
-
}
|
|
8397
|
-
};
|
|
8398
|
-
}
|
|
8399
8416
|
function clean3(value) {
|
|
8400
8417
|
const normalized = value?.trim();
|
|
8401
8418
|
return normalized || void 0;
|
|
@@ -9239,33 +9256,53 @@ var ALIASES = {
|
|
|
9239
9256
|
decision: "decision",
|
|
9240
9257
|
bug: "bug"
|
|
9241
9258
|
};
|
|
9242
|
-
var
|
|
9243
|
-
|
|
9244
|
-
"
|
|
9245
|
-
"
|
|
9246
|
-
|
|
9247
|
-
"
|
|
9248
|
-
"
|
|
9249
|
-
"
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
]
|
|
9259
|
+
var COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
9260
|
+
var ACTION_OPTIONS = {
|
|
9261
|
+
list: ["app", "q", "limit", "offset"],
|
|
9262
|
+
add: ["app", "title", "mutation-id"],
|
|
9263
|
+
get: [],
|
|
9264
|
+
set: ["mutation-id"],
|
|
9265
|
+
done: ["mutation-id"],
|
|
9266
|
+
comment: ["body", "mutation-id"],
|
|
9267
|
+
comments: [],
|
|
9268
|
+
rm: []
|
|
9269
|
+
};
|
|
9270
|
+
var ENTITY_OPTIONS = {
|
|
9271
|
+
goal: {
|
|
9272
|
+
list: ["status"],
|
|
9273
|
+
add: ["status", "proof", "target"],
|
|
9274
|
+
set: ["title", "status", "proof", "target"],
|
|
9275
|
+
done: []
|
|
9276
|
+
},
|
|
9277
|
+
task: {
|
|
9278
|
+
list: ["column", "goal", "assignee"],
|
|
9279
|
+
add: ["column", "goal", "assignee", "due", "description", "desc", "body"],
|
|
9280
|
+
set: ["title", "column", "rank", "goal", "assignee", "due", "description", "desc", "body"],
|
|
9281
|
+
done: []
|
|
9282
|
+
},
|
|
9283
|
+
decision: {
|
|
9284
|
+
list: ["status"],
|
|
9285
|
+
add: ["status", "body"],
|
|
9286
|
+
set: ["title", "status", "body"],
|
|
9287
|
+
done: []
|
|
9288
|
+
},
|
|
9289
|
+
bug: {
|
|
9290
|
+
list: ["status", "severity", "goal", "assignee", "decision"],
|
|
9291
|
+
add: ["status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
9292
|
+
set: ["title", "status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
9293
|
+
done: ["decision"]
|
|
9294
|
+
}
|
|
9295
|
+
};
|
|
9296
|
+
function canonicalAction(action) {
|
|
9297
|
+
if (action === "create") return "add";
|
|
9298
|
+
if (action === "update" || action === "status" || action === "move") return "set";
|
|
9299
|
+
if (action === "delete") return "rm";
|
|
9300
|
+
return action in ACTION_OPTIONS ? action : null;
|
|
9301
|
+
}
|
|
9302
|
+
function allowedOptions(entity, action) {
|
|
9303
|
+
const entityOptions = action === "list" || action === "add" || action === "set" || action === "done" ? ENTITY_OPTIONS[entity][action] : [];
|
|
9304
|
+
return [...COMMON_OPTIONS, ...ACTION_OPTIONS[action], ...entityOptions];
|
|
9305
|
+
}
|
|
9269
9306
|
function requireId2(id, action) {
|
|
9270
9307
|
if (!id) throw new Error(`"pm ... ${action}" needs an item id`);
|
|
9271
9308
|
return id;
|
|
@@ -9297,27 +9334,25 @@ async function buildContext2(parsed, deps) {
|
|
|
9297
9334
|
async function pmCommand(parsed, deps = {}) {
|
|
9298
9335
|
const word = parsed.positionals[1] ?? "";
|
|
9299
9336
|
if (word === "handoff") {
|
|
9300
|
-
assertArgs(parsed,
|
|
9337
|
+
assertArgs(parsed, [...COMMON_OPTIONS, "app"], 2);
|
|
9301
9338
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
9302
9339
|
}
|
|
9303
9340
|
const entity = ALIASES[word];
|
|
9304
9341
|
if (!entity) throw new Error(`unknown pm entity "${word}". Try "odla-ai pm bug list" (goal|task|decision|bug).`);
|
|
9305
|
-
const
|
|
9306
|
-
|
|
9342
|
+
const requestedAction = parsed.positionals[2] ?? "list";
|
|
9343
|
+
const action = canonicalAction(requestedAction);
|
|
9344
|
+
if (!action) throw new Error(`unknown pm action "${requestedAction}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
9345
|
+
assertArgs(parsed, allowedOptions(entity, action), 4);
|
|
9307
9346
|
const ctx = await buildContext2(parsed, deps);
|
|
9308
9347
|
const id = parsed.positionals[3];
|
|
9309
9348
|
switch (action) {
|
|
9310
9349
|
case "list":
|
|
9311
9350
|
return pmList(ctx, entity, parsed);
|
|
9312
9351
|
case "add":
|
|
9313
|
-
case "create":
|
|
9314
9352
|
return pmAdd(ctx, entity, parsed);
|
|
9315
9353
|
case "get":
|
|
9316
9354
|
return pmGet(ctx, entity, requireId2(id, action));
|
|
9317
9355
|
case "set":
|
|
9318
|
-
case "update":
|
|
9319
|
-
case "status":
|
|
9320
|
-
case "move":
|
|
9321
9356
|
return pmSet(ctx, entity, requireId2(id, action), parsed);
|
|
9322
9357
|
case "done":
|
|
9323
9358
|
return pmDone(ctx, entity, requireId2(id, action), parsed);
|
|
@@ -9326,10 +9361,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
9326
9361
|
case "comments":
|
|
9327
9362
|
return pmComments(ctx, entity, requireId2(id, action));
|
|
9328
9363
|
case "rm":
|
|
9329
|
-
case "delete":
|
|
9330
9364
|
return pmRemove(ctx, entity, requireId2(id, action));
|
|
9331
|
-
default:
|
|
9332
|
-
throw new Error(`unknown pm action "${action}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
9333
9365
|
}
|
|
9334
9366
|
}
|
|
9335
9367
|
|
|
@@ -9748,7 +9780,7 @@ function recordInvocation(parsed) {
|
|
|
9748
9780
|
try {
|
|
9749
9781
|
const entry = {
|
|
9750
9782
|
path: invocationPath(parsed.positionals),
|
|
9751
|
-
options: Object.
|
|
9783
|
+
options: Object.entries(parsed.options).map(([name, value]) => value === false ? `no-${name}` : name).sort()
|
|
9752
9784
|
};
|
|
9753
9785
|
if (!entry.path.length) return;
|
|
9754
9786
|
appendFileSync(file, `${JSON.stringify(entry)}
|
|
@@ -10521,7 +10553,7 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
10521
10553
|
}
|
|
10522
10554
|
|
|
10523
10555
|
// src/runbook-command.ts
|
|
10524
|
-
var
|
|
10556
|
+
var ALLOWED2 = [
|
|
10525
10557
|
"config",
|
|
10526
10558
|
"token",
|
|
10527
10559
|
"email",
|
|
@@ -10609,7 +10641,7 @@ async function buildContext3(parsed, deps, action) {
|
|
|
10609
10641
|
}
|
|
10610
10642
|
async function runbookCommand(parsed, deps = {}) {
|
|
10611
10643
|
const action = parsed.positionals[1] ?? "list";
|
|
10612
|
-
assertArgs(parsed,
|
|
10644
|
+
assertArgs(parsed, ALLOWED2, action === "ask" || action === "search" ? 64 : 4);
|
|
10613
10645
|
const ctx = await buildContext3(parsed, deps, action);
|
|
10614
10646
|
const slug = parsed.positionals[2];
|
|
10615
10647
|
switch (action) {
|
|
@@ -11180,7 +11212,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
11180
11212
|
return;
|
|
11181
11213
|
}
|
|
11182
11214
|
if (command === "admin") {
|
|
11183
|
-
await adminCommand(parsed);
|
|
11215
|
+
await adminCommand(parsed, runtime);
|
|
11184
11216
|
return;
|
|
11185
11217
|
}
|
|
11186
11218
|
if (command === "agent") {
|
|
@@ -11326,4 +11358,4 @@ export {
|
|
|
11326
11358
|
exitCodeFor,
|
|
11327
11359
|
runCli
|
|
11328
11360
|
};
|
|
11329
|
-
//# sourceMappingURL=chunk-
|
|
11361
|
+
//# sourceMappingURL=chunk-S3EJ66VG.js.map
|