@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
package/dist/index.cjs
CHANGED
|
@@ -975,8 +975,8 @@ function parseArgv(argv) {
|
|
|
975
975
|
}
|
|
976
976
|
return { positionals, options };
|
|
977
977
|
}
|
|
978
|
-
function assertArgs(parsed,
|
|
979
|
-
const allowed = new Set(
|
|
978
|
+
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
979
|
+
const allowed = new Set(allowedOptions2);
|
|
980
980
|
for (const name of Object.keys(parsed.options)) {
|
|
981
981
|
if (!allowed.has(name)) throw new Error(`unknown option "--${name}"; run "odla-ai help" for supported options`);
|
|
982
982
|
}
|
|
@@ -1019,66 +1019,10 @@ function addOption(options, name, value) {
|
|
|
1019
1019
|
else options[name] = [String(current), String(value)];
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
|
-
// src/
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
1027
|
-
const credentials = action === "credentials";
|
|
1028
|
-
const models = action === "models";
|
|
1029
|
-
const usage = action === "usage";
|
|
1030
|
-
const audit = action === "audit";
|
|
1031
|
-
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
1032
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
1033
|
-
}
|
|
1034
|
-
assertArgs(parsed, [
|
|
1035
|
-
"platform",
|
|
1036
|
-
"json",
|
|
1037
|
-
"open",
|
|
1038
|
-
"email",
|
|
1039
|
-
"provider",
|
|
1040
|
-
"model",
|
|
1041
|
-
"enabled",
|
|
1042
|
-
"max-input-bytes",
|
|
1043
|
-
"max-output-tokens",
|
|
1044
|
-
"max-calls-per-run",
|
|
1045
|
-
"from-env",
|
|
1046
|
-
"stdin",
|
|
1047
|
-
"discovery-provider",
|
|
1048
|
-
"discovery-model",
|
|
1049
|
-
"validation-provider",
|
|
1050
|
-
"validation-model",
|
|
1051
|
-
"app-id",
|
|
1052
|
-
"env",
|
|
1053
|
-
"run-id",
|
|
1054
|
-
"limit"
|
|
1055
|
-
], credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
1056
|
-
await adminAi({
|
|
1057
|
-
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
1058
|
-
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
1059
|
-
provider: stringOpt(parsed.options.provider),
|
|
1060
|
-
model: stringOpt(parsed.options.model),
|
|
1061
|
-
enabled: boolOpt(parsed.options.enabled),
|
|
1062
|
-
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
1063
|
-
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
1064
|
-
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
1065
|
-
platform: stringOpt(parsed.options.platform),
|
|
1066
|
-
email: stringOpt(parsed.options.email),
|
|
1067
|
-
json: parsed.options.json === true,
|
|
1068
|
-
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1069
|
-
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
1070
|
-
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
1071
|
-
stdin: parsed.options.stdin === true,
|
|
1072
|
-
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
1073
|
-
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
1074
|
-
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
1075
|
-
validationModel: stringOpt(parsed.options["validation-model"]),
|
|
1076
|
-
appId: stringOpt(parsed.options["app-id"]),
|
|
1077
|
-
env: stringOpt(parsed.options.env),
|
|
1078
|
-
runId: stringOpt(parsed.options["run-id"]),
|
|
1079
|
-
limit: numberOpt(parsed.options.limit, "--limit")
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1022
|
+
// src/operator-context.ts
|
|
1023
|
+
var import_node_fs6 = require("fs");
|
|
1024
|
+
var import_node_path6 = require("path");
|
|
1025
|
+
var import_node_process8 = __toESM(require("process"), 1);
|
|
1082
1026
|
|
|
1083
1027
|
// src/config.ts
|
|
1084
1028
|
var import_node_fs4 = require("fs");
|
|
@@ -1364,6 +1308,291 @@ function unique2(values) {
|
|
|
1364
1308
|
return [...new Set(values.filter(Boolean))];
|
|
1365
1309
|
}
|
|
1366
1310
|
|
|
1311
|
+
// src/operator-profiles.ts
|
|
1312
|
+
var import_node_fs5 = require("fs");
|
|
1313
|
+
var import_node_os = require("os");
|
|
1314
|
+
var import_node_path5 = require("path");
|
|
1315
|
+
var import_node_process7 = __toESM(require("process"), 1);
|
|
1316
|
+
function operatorProfileFile() {
|
|
1317
|
+
return (0, import_node_path5.resolve)(
|
|
1318
|
+
clean(import_node_process7.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path5.join)((0, import_node_os.homedir)(), ".odla", "contexts.json")
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
function resolveOperatorProfile(parsed) {
|
|
1322
|
+
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1323
|
+
const fromEnvironment = clean(import_node_process7.default.env.ODLA_CONTEXT);
|
|
1324
|
+
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1325
|
+
const file = operatorProfileFile();
|
|
1326
|
+
if (!name) {
|
|
1327
|
+
return { name: null, source: "unresolved", file, value: null };
|
|
1328
|
+
}
|
|
1329
|
+
assertOperatorName(name, "context");
|
|
1330
|
+
const profiles = readOperatorProfiles(file);
|
|
1331
|
+
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
1332
|
+
if (!value) {
|
|
1333
|
+
throw new Error(
|
|
1334
|
+
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
name,
|
|
1339
|
+
source: fromFlag ? "flag" : "environment",
|
|
1340
|
+
file,
|
|
1341
|
+
value
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
1345
|
+
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
1346
|
+
}
|
|
1347
|
+
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
1348
|
+
assertOperatorName(name, "context");
|
|
1349
|
+
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
1350
|
+
const profiles = readOperatorProfiles(file);
|
|
1351
|
+
profiles[name] = normalized;
|
|
1352
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1353
|
+
}
|
|
1354
|
+
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
1355
|
+
assertOperatorName(name, "context");
|
|
1356
|
+
const profiles = readOperatorProfiles(file);
|
|
1357
|
+
if (!Object.hasOwn(profiles, name)) return false;
|
|
1358
|
+
delete profiles[name];
|
|
1359
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1360
|
+
return true;
|
|
1361
|
+
}
|
|
1362
|
+
function operatorCredentialFiles(selection) {
|
|
1363
|
+
const base = selection.name ? (0, import_node_path5.join)((0, import_node_path5.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path5.join)((0, import_node_os.homedir)(), ".odla");
|
|
1364
|
+
return {
|
|
1365
|
+
developer: (0, import_node_path5.join)(base, "dev-token.json"),
|
|
1366
|
+
scoped: (0, import_node_path5.join)(base, "admin-token.local.json")
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function assertOperatorName(value, label) {
|
|
1370
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
1371
|
+
throw new Error(
|
|
1372
|
+
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
function readOperatorProfiles(file) {
|
|
1377
|
+
if (!(0, import_node_fs5.existsSync)(file)) return emptyProfiles();
|
|
1378
|
+
let raw;
|
|
1379
|
+
try {
|
|
1380
|
+
raw = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
|
|
1381
|
+
} catch {
|
|
1382
|
+
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1383
|
+
}
|
|
1384
|
+
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
1385
|
+
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
1386
|
+
}
|
|
1387
|
+
const unknown = Object.keys(raw).filter(
|
|
1388
|
+
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
1389
|
+
);
|
|
1390
|
+
if (unknown.length > 0) {
|
|
1391
|
+
throw new Error(
|
|
1392
|
+
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
const profiles = emptyProfiles();
|
|
1396
|
+
for (const [name, value] of Object.entries(
|
|
1397
|
+
raw.profiles
|
|
1398
|
+
)) {
|
|
1399
|
+
assertOperatorName(name, "context");
|
|
1400
|
+
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
1401
|
+
}
|
|
1402
|
+
return profiles;
|
|
1403
|
+
}
|
|
1404
|
+
function emptyProfiles() {
|
|
1405
|
+
return /* @__PURE__ */ Object.create(null);
|
|
1406
|
+
}
|
|
1407
|
+
function validateProfile(value, label) {
|
|
1408
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1409
|
+
throw new Error(`${label} has an invalid shape`);
|
|
1410
|
+
}
|
|
1411
|
+
const unknown = Object.keys(value).filter(
|
|
1412
|
+
(key) => !["platform", "app", "environment"].includes(key)
|
|
1413
|
+
);
|
|
1414
|
+
if (unknown.length > 0) {
|
|
1415
|
+
throw new Error(
|
|
1416
|
+
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
const candidate = value;
|
|
1420
|
+
if (typeof candidate.platform !== "string") {
|
|
1421
|
+
throw new Error(`${label} needs an absolute platform URL`);
|
|
1422
|
+
}
|
|
1423
|
+
const platform = platformAudience(candidate.platform);
|
|
1424
|
+
const app = clean(candidate.app);
|
|
1425
|
+
const environment2 = clean(candidate.environment);
|
|
1426
|
+
if (app) assertOperatorName(app, "app");
|
|
1427
|
+
if (environment2) assertOperatorName(environment2, "environment");
|
|
1428
|
+
return {
|
|
1429
|
+
platform,
|
|
1430
|
+
...app ? { app } : {},
|
|
1431
|
+
...environment2 ? { environment: environment2 } : {}
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function clean(value) {
|
|
1435
|
+
const normalized = value?.trim();
|
|
1436
|
+
return normalized || void 0;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/operator-context.ts
|
|
1440
|
+
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
1441
|
+
async function resolveOperatorContext(parsed, options = {}) {
|
|
1442
|
+
const profile = resolveOperatorProfile(parsed);
|
|
1443
|
+
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1444
|
+
const configPath = (0, import_node_path6.resolve)(configArgument);
|
|
1445
|
+
const explicitConfig = parsed.options.config !== void 0;
|
|
1446
|
+
const hasConfig = (0, import_node_fs6.existsSync)(configPath);
|
|
1447
|
+
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1448
|
+
await loadProjectConfig(configArgument);
|
|
1449
|
+
}
|
|
1450
|
+
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1451
|
+
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1452
|
+
const platformEnvironment = clean2(import_node_process8.default.env.ODLA_PLATFORM_URL);
|
|
1453
|
+
const platformValue = platformAudience(
|
|
1454
|
+
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1455
|
+
);
|
|
1456
|
+
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1457
|
+
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1458
|
+
const appEnvironment = clean2(import_node_process8.default.env.ODLA_APP_ID);
|
|
1459
|
+
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1460
|
+
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1461
|
+
if (appValue) assertOperatorName(appValue, "app");
|
|
1462
|
+
if (options.requireApp && !appValue) {
|
|
1463
|
+
throw new Error(
|
|
1464
|
+
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1468
|
+
const envEnvironment = clean2(import_node_process8.default.env.ODLA_ENV);
|
|
1469
|
+
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1470
|
+
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1471
|
+
if (environmentValue) {
|
|
1472
|
+
assertOperatorName(environmentValue, "environment");
|
|
1473
|
+
}
|
|
1474
|
+
const rootDir = loaded?.rootDir ?? import_node_process8.default.cwd();
|
|
1475
|
+
const profileCredentials = operatorCredentialFiles(profile);
|
|
1476
|
+
const tokenFile = clean2(import_node_process8.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path6.resolve)(import_node_process8.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
1477
|
+
const scopedTokenFile = clean2(import_node_process8.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path6.resolve)(import_node_process8.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path6.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
1478
|
+
const cfg = loaded ? {
|
|
1479
|
+
...loaded,
|
|
1480
|
+
platformUrl: platformValue,
|
|
1481
|
+
app: appValue ? {
|
|
1482
|
+
id: appValue,
|
|
1483
|
+
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
1484
|
+
} : loaded.app,
|
|
1485
|
+
local: { ...loaded.local, tokenFile }
|
|
1486
|
+
} : {
|
|
1487
|
+
configPath,
|
|
1488
|
+
rootDir,
|
|
1489
|
+
platformUrl: platformValue,
|
|
1490
|
+
dbEndpoint: platformValue,
|
|
1491
|
+
app: {
|
|
1492
|
+
id: appValue ?? "operator",
|
|
1493
|
+
name: appValue ?? "Operator"
|
|
1494
|
+
},
|
|
1495
|
+
envs: environmentValue ? [environmentValue] : [],
|
|
1496
|
+
services: [],
|
|
1497
|
+
local: {
|
|
1498
|
+
tokenFile,
|
|
1499
|
+
credentialsFile: (0, import_node_path6.join)(rootDir, ".odla", "credentials.local.json"),
|
|
1500
|
+
devVarsFile: (0, import_node_path6.join)(rootDir, ".dev.vars"),
|
|
1501
|
+
gitignore: true
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
return {
|
|
1505
|
+
cfg,
|
|
1506
|
+
profile: {
|
|
1507
|
+
name: profile.name,
|
|
1508
|
+
source: profile.source,
|
|
1509
|
+
file: profile.file
|
|
1510
|
+
},
|
|
1511
|
+
config: {
|
|
1512
|
+
path: configPath,
|
|
1513
|
+
status: loaded ? "loaded" : "absent",
|
|
1514
|
+
explicit: explicitConfig
|
|
1515
|
+
},
|
|
1516
|
+
platform: { value: platformValue, source: platformSource },
|
|
1517
|
+
app: { value: appValue, source: appSource },
|
|
1518
|
+
environment: {
|
|
1519
|
+
value: environmentValue,
|
|
1520
|
+
source: environmentSource
|
|
1521
|
+
},
|
|
1522
|
+
credentials: {
|
|
1523
|
+
developerTokenFile: tokenFile,
|
|
1524
|
+
scopedTokenFile
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function clean2(value) {
|
|
1529
|
+
const normalized = value?.trim();
|
|
1530
|
+
return normalized || void 0;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// src/admin-command.ts
|
|
1534
|
+
var CONTEXT_OPTIONS = ["platform", "config", "context", "token", "open", "email"];
|
|
1535
|
+
var JSON_OPTIONS = [...CONTEXT_OPTIONS, "json"];
|
|
1536
|
+
var SET_OPTIONS = [
|
|
1537
|
+
...JSON_OPTIONS,
|
|
1538
|
+
"provider",
|
|
1539
|
+
"model",
|
|
1540
|
+
"enabled",
|
|
1541
|
+
"max-input-bytes",
|
|
1542
|
+
"max-output-tokens",
|
|
1543
|
+
"max-calls-per-run",
|
|
1544
|
+
"discovery-provider",
|
|
1545
|
+
"discovery-model",
|
|
1546
|
+
"validation-provider",
|
|
1547
|
+
"validation-model"
|
|
1548
|
+
];
|
|
1549
|
+
async function adminCommand(parsed, deps = {}) {
|
|
1550
|
+
const area = parsed.positionals[1];
|
|
1551
|
+
const action = parsed.positionals[2];
|
|
1552
|
+
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
1553
|
+
const credentials = action === "credentials";
|
|
1554
|
+
const models = action === "models";
|
|
1555
|
+
const usage = action === "usage";
|
|
1556
|
+
const audit = action === "audit";
|
|
1557
|
+
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
1558
|
+
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
1559
|
+
}
|
|
1560
|
+
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;
|
|
1561
|
+
assertArgs(parsed, allowed, credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
1562
|
+
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
1563
|
+
await adminAi({
|
|
1564
|
+
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
1565
|
+
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
1566
|
+
provider: stringOpt(parsed.options.provider),
|
|
1567
|
+
model: stringOpt(parsed.options.model),
|
|
1568
|
+
enabled: boolOpt(parsed.options.enabled),
|
|
1569
|
+
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
1570
|
+
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
1571
|
+
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
1572
|
+
platform: context.platform.value,
|
|
1573
|
+
token: stringOpt(parsed.options.token),
|
|
1574
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
1575
|
+
email: stringOpt(parsed.options.email),
|
|
1576
|
+
json: parsed.options.json === true,
|
|
1577
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1578
|
+
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
1579
|
+
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
1580
|
+
stdin: parsed.options.stdin === true,
|
|
1581
|
+
readStdin: deps.readStdin,
|
|
1582
|
+
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
1583
|
+
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
1584
|
+
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
1585
|
+
validationModel: stringOpt(parsed.options["validation-model"]),
|
|
1586
|
+
appId: stringOpt(parsed.options["app-id"]),
|
|
1587
|
+
env: stringOpt(parsed.options.env),
|
|
1588
|
+
runId: stringOpt(parsed.options["run-id"]),
|
|
1589
|
+
limit: numberOpt(parsed.options.limit, "--limit"),
|
|
1590
|
+
fetch: deps.fetch,
|
|
1591
|
+
stdout: deps.stdout,
|
|
1592
|
+
openApprovalUrl: deps.openUrl
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1367
1596
|
// src/tenant.ts
|
|
1368
1597
|
var import_apps = require("@odla-ai/apps");
|
|
1369
1598
|
function resolveEnv(cfg, requested) {
|
|
@@ -1469,7 +1698,7 @@ function errorMessage(body) {
|
|
|
1469
1698
|
}
|
|
1470
1699
|
|
|
1471
1700
|
// src/app-export.ts
|
|
1472
|
-
var
|
|
1701
|
+
var import_node_fs7 = require("fs");
|
|
1473
1702
|
var import_node_stream = require("stream");
|
|
1474
1703
|
var import_promises = require("stream/promises");
|
|
1475
1704
|
async function appExport(options) {
|
|
@@ -1502,7 +1731,7 @@ async function appExport(options) {
|
|
|
1502
1731
|
const download = await doFetch(`${base}/backups/${newest.id}/download`, { headers: auth });
|
|
1503
1732
|
if (!download.ok || !download.body) throw new Error(`download failed (${download.status})`);
|
|
1504
1733
|
const file = options.out ?? `${tenant}-${new Date(newest.created_at).toISOString().slice(0, 10)}-tx${newest.max_tx}.jsonl.gz`;
|
|
1505
|
-
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(download.body), (0,
|
|
1734
|
+
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(download.body), (0, import_node_fs7.createWriteStream)(file));
|
|
1506
1735
|
if (options.json) out.log(JSON.stringify({ file, backup: newest }, null, 2));
|
|
1507
1736
|
else {
|
|
1508
1737
|
out.log(`${tenant}: wrote ${file} (${newest.bytes} bytes, ${newest.kind} snapshot at tx ${newest.max_tx})`);
|
|
@@ -1512,7 +1741,7 @@ async function appExport(options) {
|
|
|
1512
1741
|
}
|
|
1513
1742
|
|
|
1514
1743
|
// src/app-import.ts
|
|
1515
|
-
var
|
|
1744
|
+
var import_node_fs8 = require("fs");
|
|
1516
1745
|
var import_import = require("@odla-ai/db/import");
|
|
1517
1746
|
function chooseIdMode(options, rows) {
|
|
1518
1747
|
const chosen = [options.idField && "field", options.key && "key", options.generateIds && "generate"].filter(Boolean);
|
|
@@ -1531,7 +1760,7 @@ async function appImport(options) {
|
|
|
1531
1760
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
1532
1761
|
const doFetch = options.fetch ?? fetch;
|
|
1533
1762
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
1534
|
-
const text = options.file === "-" ? (options.readStdin ?? (() => (0,
|
|
1763
|
+
const text = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs8.readFileSync)(0, "utf8")))() : (0, import_node_fs8.readFileSync)(options.file, "utf8");
|
|
1535
1764
|
const { format, sources } = (0, import_import.parseImport)(text, options.ns);
|
|
1536
1765
|
if (format === "namespace-map" && options.ns) {
|
|
1537
1766
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2498,13 +2727,13 @@ function printGroup(out, heading, items) {
|
|
|
2498
2727
|
|
|
2499
2728
|
// src/doctor-checks.ts
|
|
2500
2729
|
var import_node_child_process3 = require("child_process");
|
|
2501
|
-
var
|
|
2502
|
-
var
|
|
2730
|
+
var import_node_fs10 = require("fs");
|
|
2731
|
+
var import_node_path8 = require("path");
|
|
2503
2732
|
|
|
2504
2733
|
// src/wrangler.ts
|
|
2505
2734
|
var import_node_child_process2 = require("child_process");
|
|
2506
|
-
var
|
|
2507
|
-
var
|
|
2735
|
+
var import_node_fs9 = require("fs");
|
|
2736
|
+
var import_node_path7 = require("path");
|
|
2508
2737
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
2509
2738
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
2510
2739
|
let stdout = "";
|
|
@@ -2518,15 +2747,15 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
2518
2747
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
2519
2748
|
function findWranglerConfig(rootDir) {
|
|
2520
2749
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
2521
|
-
const path = (0,
|
|
2522
|
-
if ((0,
|
|
2750
|
+
const path = (0, import_node_path7.join)(rootDir, name);
|
|
2751
|
+
if ((0, import_node_fs9.existsSync)(path)) return path;
|
|
2523
2752
|
}
|
|
2524
2753
|
return null;
|
|
2525
2754
|
}
|
|
2526
2755
|
function readWranglerConfig(path) {
|
|
2527
2756
|
if (path.endsWith(".toml")) return null;
|
|
2528
2757
|
try {
|
|
2529
|
-
return JSON.parse(stripJsonComments((0,
|
|
2758
|
+
return JSON.parse(stripJsonComments((0, import_node_fs9.readFileSync)(path, "utf8")));
|
|
2530
2759
|
} catch {
|
|
2531
2760
|
return null;
|
|
2532
2761
|
}
|
|
@@ -2624,10 +2853,10 @@ function wranglerWarnings(rootDir) {
|
|
|
2624
2853
|
for (const { label, block } of blocks) {
|
|
2625
2854
|
const assets = block.assets;
|
|
2626
2855
|
if (assets?.directory) {
|
|
2627
|
-
const dir = (0,
|
|
2628
|
-
if (dir === (0,
|
|
2856
|
+
const dir = (0, import_node_path8.resolve)(rootDir, assets.directory);
|
|
2857
|
+
if (dir === (0, import_node_path8.resolve)(rootDir)) {
|
|
2629
2858
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
2630
|
-
} else if ((0,
|
|
2859
|
+
} else if ((0, import_node_fs10.existsSync)((0, import_node_path8.join)(dir, "node_modules"))) {
|
|
2631
2860
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
2632
2861
|
}
|
|
2633
2862
|
}
|
|
@@ -2662,13 +2891,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
2662
2891
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
2663
2892
|
return warnings;
|
|
2664
2893
|
}
|
|
2665
|
-
const main = typeof config.main === "string" ? (0,
|
|
2666
|
-
if (!main || !(0,
|
|
2894
|
+
const main = typeof config.main === "string" ? (0, import_node_path8.resolve)(rootDir, config.main) : null;
|
|
2895
|
+
if (!main || !(0, import_node_fs10.existsSync)(main)) {
|
|
2667
2896
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
2668
2897
|
} else {
|
|
2669
2898
|
let source = "";
|
|
2670
2899
|
try {
|
|
2671
|
-
source = (0,
|
|
2900
|
+
source = (0, import_node_fs10.readFileSync)(main, "utf8");
|
|
2672
2901
|
} catch {
|
|
2673
2902
|
}
|
|
2674
2903
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -2692,7 +2921,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
2692
2921
|
}
|
|
2693
2922
|
function readPackageJson(rootDir) {
|
|
2694
2923
|
try {
|
|
2695
|
-
return JSON.parse((0,
|
|
2924
|
+
return JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path8.join)(rootDir, "package.json"), "utf8"));
|
|
2696
2925
|
} catch {
|
|
2697
2926
|
return null;
|
|
2698
2927
|
}
|
|
@@ -2919,13 +3148,13 @@ function harnessOption(value, flag) {
|
|
|
2919
3148
|
}
|
|
2920
3149
|
|
|
2921
3150
|
// src/init.ts
|
|
2922
|
-
var
|
|
2923
|
-
var
|
|
3151
|
+
var import_node_fs11 = require("fs");
|
|
3152
|
+
var import_node_path9 = require("path");
|
|
2924
3153
|
function initProject(options) {
|
|
2925
3154
|
const out = options.stdout ?? console;
|
|
2926
|
-
const rootDir = (0,
|
|
2927
|
-
const configPath = (0,
|
|
2928
|
-
if ((0,
|
|
3155
|
+
const rootDir = (0, import_node_path9.resolve)(options.rootDir ?? process.cwd());
|
|
3156
|
+
const configPath = (0, import_node_path9.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
3157
|
+
if ((0, import_node_fs11.existsSync)(configPath) && !options.force) {
|
|
2929
3158
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
2930
3159
|
}
|
|
2931
3160
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -2935,20 +3164,20 @@ function initProject(options) {
|
|
|
2935
3164
|
const services = options.services?.length ? options.services : ["db", "ai"];
|
|
2936
3165
|
if (services.includes("calendar") && !services.includes("db")) throw new Error("--services calendar requires db");
|
|
2937
3166
|
const aiProvider = options.aiProvider ?? "anthropic";
|
|
2938
|
-
(0,
|
|
2939
|
-
(0,
|
|
2940
|
-
(0,
|
|
2941
|
-
(0,
|
|
2942
|
-
writeIfMissing((0,
|
|
2943
|
-
writeIfMissing((0,
|
|
3167
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.dirname)(configPath), { recursive: true });
|
|
3168
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
3169
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, ".odla"), { recursive: true });
|
|
3170
|
+
(0, import_node_fs11.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
3171
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
3172
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
2944
3173
|
ensureGitignore(rootDir);
|
|
2945
3174
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
2946
3175
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
2947
3176
|
out.log("updated .gitignore for local odla credentials");
|
|
2948
3177
|
}
|
|
2949
3178
|
function writeIfMissing(path, text) {
|
|
2950
|
-
if ((0,
|
|
2951
|
-
(0,
|
|
3179
|
+
if ((0, import_node_fs11.existsSync)(path)) return;
|
|
3180
|
+
(0, import_node_fs11.writeFileSync)(path, text);
|
|
2952
3181
|
}
|
|
2953
3182
|
function configTemplate(input) {
|
|
2954
3183
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -3179,9 +3408,9 @@ async function resolveVaultWrite(options) {
|
|
|
3179
3408
|
}
|
|
3180
3409
|
|
|
3181
3410
|
// src/skill.ts
|
|
3182
|
-
var
|
|
3183
|
-
var
|
|
3184
|
-
var
|
|
3411
|
+
var import_node_fs12 = require("fs");
|
|
3412
|
+
var import_node_os2 = require("os");
|
|
3413
|
+
var import_node_path10 = require("path");
|
|
3185
3414
|
var import_node_url2 = require("url");
|
|
3186
3415
|
|
|
3187
3416
|
// src/skill-adapters.ts
|
|
@@ -3260,8 +3489,8 @@ function installSkill(options = {}) {
|
|
|
3260
3489
|
const files = listFiles(sourceDir);
|
|
3261
3490
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
3262
3491
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
3263
|
-
const root = (0,
|
|
3264
|
-
const home = (0,
|
|
3492
|
+
const root = (0, import_node_path10.resolve)(options.dir ?? process.cwd());
|
|
3493
|
+
const home = (0, import_node_path10.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
|
|
3265
3494
|
const plans = /* @__PURE__ */ new Map();
|
|
3266
3495
|
const targets = /* @__PURE__ */ new Map();
|
|
3267
3496
|
const rememberTarget = (harness, target) => {
|
|
@@ -3275,48 +3504,48 @@ function installSkill(options = {}) {
|
|
|
3275
3504
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
3276
3505
|
};
|
|
3277
3506
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
3278
|
-
for (const rel of files) plan((0,
|
|
3507
|
+
for (const rel of files) plan((0, import_node_path10.join)(targetDir2, rel), (0, import_node_fs12.readFileSync)((0, import_node_path10.join)(sourceDir, rel), "utf8"), false, boundary);
|
|
3279
3508
|
};
|
|
3280
3509
|
let targetDir;
|
|
3281
3510
|
if (options.global) {
|
|
3282
|
-
const claudeRoot = (0,
|
|
3283
|
-
const codexRoot = (0,
|
|
3511
|
+
const claudeRoot = (0, import_node_path10.join)(home, ".claude", "skills");
|
|
3512
|
+
const codexRoot = (0, import_node_path10.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path10.join)(home, ".codex"), "skills");
|
|
3284
3513
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
3285
3514
|
for (const harness of harnesses) {
|
|
3286
3515
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
3287
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
3516
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path10.dirname)((0, import_node_path10.dirname)(codexRoot)));
|
|
3288
3517
|
rememberTarget(harness, skillRoot);
|
|
3289
3518
|
}
|
|
3290
3519
|
} else {
|
|
3291
|
-
const sharedRoot = (0,
|
|
3520
|
+
const sharedRoot = (0, import_node_path10.join)(root, ".agents", "skills");
|
|
3292
3521
|
planSkillTree(sharedRoot);
|
|
3293
|
-
const claudeRoot = (0,
|
|
3522
|
+
const claudeRoot = (0, import_node_path10.join)(root, ".claude", "skills");
|
|
3294
3523
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
3295
3524
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
3296
3525
|
if (harnesses.includes("claude")) {
|
|
3297
3526
|
for (const skill of skillNames(files)) {
|
|
3298
|
-
const canonical = (0,
|
|
3299
|
-
plan((0,
|
|
3527
|
+
const canonical = (0, import_node_fs12.readFileSync)((0, import_node_path10.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
3528
|
+
plan((0, import_node_path10.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
|
|
3300
3529
|
}
|
|
3301
3530
|
rememberTarget("claude", claudeRoot);
|
|
3302
3531
|
}
|
|
3303
3532
|
if (harnesses.includes("cursor")) {
|
|
3304
|
-
const cursorRule = (0,
|
|
3533
|
+
const cursorRule = (0, import_node_path10.join)(root, ".cursor", "rules", "odla.mdc");
|
|
3305
3534
|
plan(cursorRule, CURSOR_RULE);
|
|
3306
3535
|
rememberTarget("cursor", cursorRule);
|
|
3307
3536
|
}
|
|
3308
3537
|
if (harnesses.includes("agents")) {
|
|
3309
|
-
const agentsFile = (0,
|
|
3538
|
+
const agentsFile = (0, import_node_path10.join)(root, "AGENTS.md");
|
|
3310
3539
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3311
3540
|
rememberTarget("agents", agentsFile);
|
|
3312
3541
|
}
|
|
3313
3542
|
if (harnesses.includes("copilot")) {
|
|
3314
|
-
const copilotFile = (0,
|
|
3543
|
+
const copilotFile = (0, import_node_path10.join)(root, ".github", "copilot-instructions.md");
|
|
3315
3544
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3316
3545
|
rememberTarget("copilot", copilotFile);
|
|
3317
3546
|
}
|
|
3318
3547
|
if (harnesses.includes("gemini")) {
|
|
3319
|
-
const geminiFile = (0,
|
|
3548
|
+
const geminiFile = (0, import_node_path10.join)(root, "GEMINI.md");
|
|
3320
3549
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3321
3550
|
rememberTarget("gemini", geminiFile);
|
|
3322
3551
|
}
|
|
@@ -3330,11 +3559,11 @@ function installSkill(options = {}) {
|
|
|
3330
3559
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
3331
3560
|
continue;
|
|
3332
3561
|
}
|
|
3333
|
-
if (!(0,
|
|
3562
|
+
if (!(0, import_node_fs12.existsSync)(file.target)) {
|
|
3334
3563
|
writtenPaths.add(file.target);
|
|
3335
3564
|
continue;
|
|
3336
3565
|
}
|
|
3337
|
-
const current = (0,
|
|
3566
|
+
const current = (0, import_node_fs12.readFileSync)(file.target, "utf8");
|
|
3338
3567
|
if (current === file.content) {
|
|
3339
3568
|
unchangedPaths.add(file.target);
|
|
3340
3569
|
} else if (file.managedMerge || options.force) {
|
|
@@ -3351,9 +3580,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3351
3580
|
);
|
|
3352
3581
|
}
|
|
3353
3582
|
for (const file of plans.values()) {
|
|
3354
|
-
if (!(0,
|
|
3355
|
-
(0,
|
|
3356
|
-
(0,
|
|
3583
|
+
if (!(0, import_node_fs12.existsSync)(file.target) || (0, import_node_fs12.readFileSync)(file.target, "utf8") !== file.content) {
|
|
3584
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(file.target), { recursive: true });
|
|
3585
|
+
(0, import_node_fs12.writeFileSync)(file.target, file.content);
|
|
3357
3586
|
}
|
|
3358
3587
|
}
|
|
3359
3588
|
const skills = skillNames(files);
|
|
@@ -3372,7 +3601,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3372
3601
|
};
|
|
3373
3602
|
}
|
|
3374
3603
|
function pathsUnder(root, paths) {
|
|
3375
|
-
return [...paths].map((path) => (0,
|
|
3604
|
+
return [...paths].map((path) => (0, import_node_path10.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path10.sep}`) && !(0, import_node_path10.isAbsolute)(path)).sort();
|
|
3376
3605
|
}
|
|
3377
3606
|
function normalizeHarnesses(values, global) {
|
|
3378
3607
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -3394,9 +3623,9 @@ function normalizeHarnesses(values, global) {
|
|
|
3394
3623
|
function managedFileContent(path, block, force, boundary) {
|
|
3395
3624
|
const symlink = symlinkedComponent(boundary, path);
|
|
3396
3625
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
3397
|
-
if (!(0,
|
|
3626
|
+
if (!(0, import_node_fs12.existsSync)(path)) return `${block}
|
|
3398
3627
|
`;
|
|
3399
|
-
const current = (0,
|
|
3628
|
+
const current = (0, import_node_fs12.readFileSync)(path, "utf8");
|
|
3400
3629
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
3401
3630
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
3402
3631
|
const startAt = current.indexOf(start);
|
|
@@ -3417,15 +3646,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
3417
3646
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
3418
3647
|
}
|
|
3419
3648
|
function symlinkedComponent(boundary, target) {
|
|
3420
|
-
const rel = (0,
|
|
3421
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
3649
|
+
const rel = (0, import_node_path10.relative)(boundary, target);
|
|
3650
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(rel)) {
|
|
3422
3651
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
3423
3652
|
}
|
|
3424
3653
|
let current = boundary;
|
|
3425
|
-
for (const part of rel.split(
|
|
3426
|
-
current = (0,
|
|
3654
|
+
for (const part of rel.split(import_node_path10.sep).filter(Boolean)) {
|
|
3655
|
+
current = (0, import_node_path10.join)(current, part);
|
|
3427
3656
|
try {
|
|
3428
|
-
if ((0,
|
|
3657
|
+
if ((0, import_node_fs12.lstatSync)(current).isSymbolicLink()) return current;
|
|
3429
3658
|
} catch (error) {
|
|
3430
3659
|
if (error.code !== "ENOENT") throw error;
|
|
3431
3660
|
}
|
|
@@ -3436,13 +3665,13 @@ function skillNames(files) {
|
|
|
3436
3665
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
3437
3666
|
}
|
|
3438
3667
|
function listFiles(dir) {
|
|
3439
|
-
if (!(0,
|
|
3668
|
+
if (!(0, import_node_fs12.existsSync)(dir)) return [];
|
|
3440
3669
|
const results = [];
|
|
3441
3670
|
const walk = (current) => {
|
|
3442
|
-
for (const entry of (0,
|
|
3443
|
-
const path = (0,
|
|
3671
|
+
for (const entry of (0, import_node_fs12.readdirSync)(current, { withFileTypes: true })) {
|
|
3672
|
+
const path = (0, import_node_path10.join)(current, entry.name);
|
|
3444
3673
|
if (entry.isDirectory()) walk(path);
|
|
3445
|
-
else results.push((0,
|
|
3674
|
+
else results.push((0, import_node_path10.relative)(dir, path));
|
|
3446
3675
|
}
|
|
3447
3676
|
};
|
|
3448
3677
|
walk(dir);
|
|
@@ -3589,13 +3818,16 @@ async function safeText3(res) {
|
|
|
3589
3818
|
|
|
3590
3819
|
// src/cli-project.ts
|
|
3591
3820
|
var SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
3592
|
-
function install(parsed, positionals) {
|
|
3821
|
+
function install(parsed, positionals, deps) {
|
|
3593
3822
|
assertArgs(parsed, SKILL_OPTS, positionals);
|
|
3594
3823
|
installSkill({
|
|
3595
3824
|
dir: stringOpt(parsed.options.dir),
|
|
3596
3825
|
global: parsed.options.global === true,
|
|
3597
3826
|
harnesses: harnessList(parsed),
|
|
3598
|
-
force: parsed.options.force === true
|
|
3827
|
+
force: parsed.options.force === true,
|
|
3828
|
+
homeDir: deps.homeDir,
|
|
3829
|
+
codexHomeDir: deps.codexHomeDir,
|
|
3830
|
+
stdout: deps.stdout
|
|
3599
3831
|
});
|
|
3600
3832
|
}
|
|
3601
3833
|
async function secretsCommand(parsed, deps) {
|
|
@@ -3611,6 +3843,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
3611
3843
|
token: stringOpt(parsed.options.token),
|
|
3612
3844
|
email: stringOpt(parsed.options.email),
|
|
3613
3845
|
yes: parsed.options.yes === true,
|
|
3846
|
+
readStdin: deps.readStdin,
|
|
3614
3847
|
fetch: deps.fetch,
|
|
3615
3848
|
stdout: deps.stdout
|
|
3616
3849
|
};
|
|
@@ -3669,8 +3902,8 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3669
3902
|
return true;
|
|
3670
3903
|
}
|
|
3671
3904
|
if (command === "setup") {
|
|
3672
|
-
install(parsed, 1);
|
|
3673
|
-
console.log(
|
|
3905
|
+
install(parsed, 1, deps);
|
|
3906
|
+
(deps.stdout ?? console).log(
|
|
3674
3907
|
"\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."
|
|
3675
3908
|
);
|
|
3676
3909
|
return true;
|
|
@@ -3678,7 +3911,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3678
3911
|
if (command === "skill") {
|
|
3679
3912
|
const sub = parsed.positionals[1];
|
|
3680
3913
|
if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
|
|
3681
|
-
install(parsed, 2);
|
|
3914
|
+
install(parsed, 2, deps);
|
|
3682
3915
|
return true;
|
|
3683
3916
|
}
|
|
3684
3917
|
if (command === "secrets") {
|
|
@@ -3689,9 +3922,9 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3689
3922
|
}
|
|
3690
3923
|
|
|
3691
3924
|
// src/code-connect.ts
|
|
3692
|
-
var
|
|
3693
|
-
var
|
|
3694
|
-
var
|
|
3925
|
+
var import_node_fs14 = require("fs");
|
|
3926
|
+
var import_node_os4 = require("os");
|
|
3927
|
+
var import_node_path12 = require("path");
|
|
3695
3928
|
|
|
3696
3929
|
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
3697
3930
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -6576,9 +6809,9 @@ var CodePiRuntimeEngine = class {
|
|
|
6576
6809
|
};
|
|
6577
6810
|
|
|
6578
6811
|
// src/help.ts
|
|
6579
|
-
var
|
|
6812
|
+
var import_node_fs13 = require("fs");
|
|
6580
6813
|
function cliVersion() {
|
|
6581
|
-
const pkg = JSON.parse((0,
|
|
6814
|
+
const pkg = JSON.parse((0, import_node_fs13.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
|
|
6582
6815
|
return pkg.version ?? "unknown";
|
|
6583
6816
|
}
|
|
6584
6817
|
function printHelp(output = console) {
|
|
@@ -6613,11 +6846,21 @@ Usage:
|
|
|
6613
6846
|
odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
6614
6847
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
6615
6848
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
6616
|
-
odla-ai pm
|
|
6617
|
-
odla-ai pm
|
|
6849
|
+
odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6850
|
+
odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6851
|
+
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6852
|
+
odla-ai pm bug list [--app <id>] [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6853
|
+
odla-ai pm goal add --app <id> --title <t> [--status <s>] [--proof <text>] [--target <pct>] [--mutation-id <id>] [--json]
|
|
6854
|
+
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]
|
|
6855
|
+
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
6856
|
+
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]
|
|
6618
6857
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
6619
|
-
odla-ai pm
|
|
6620
|
-
odla-ai pm
|
|
6858
|
+
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
6859
|
+
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]
|
|
6860
|
+
odla-ai pm decision set <id> [--title <t>|--status <s>|--body <text>] [--mutation-id <id>] [--json]
|
|
6861
|
+
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]
|
|
6862
|
+
odla-ai pm <goal|task|decision> done <id> [--mutation-id <id>]
|
|
6863
|
+
odla-ai pm bug done <id> [--decision <accepted-decision-id>] [--mutation-id <id>]
|
|
6621
6864
|
odla-ai pm <goal|task|decision|bug> comment <id> --body "..." [--mutation-id <id>]
|
|
6622
6865
|
odla-ai pm <goal|task|decision|bug> comments <id> [--json]
|
|
6623
6866
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
@@ -6656,15 +6899,16 @@ Usage:
|
|
|
6656
6899
|
odla-ai runbook rm <slug> [--app <id>]
|
|
6657
6900
|
odla-ai capabilities [--json]
|
|
6658
6901
|
odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
|
|
6659
|
-
odla-ai admin ai show [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6660
|
-
odla-ai admin ai models [--provider <id>] [--json]
|
|
6661
|
-
odla-ai admin ai set <purpose> [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
odla-ai admin ai
|
|
6666
|
-
odla-ai admin ai
|
|
6667
|
-
odla-ai admin ai
|
|
6902
|
+
odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6903
|
+
odla-ai admin ai models [--context <name>] [--provider <id>] [--json]
|
|
6904
|
+
odla-ai admin ai set <purpose> [--context <name>] [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6905
|
+
[--max-input-bytes <n>] [--max-output-tokens <n>] [--max-calls-per-run <n>] [--json]
|
|
6906
|
+
odla-ai admin ai set security [--context <name>] --discovery-provider <id> --discovery-model <id>
|
|
6907
|
+
--validation-provider <id> --validation-model <id> [--enabled|--no-enabled] [--json]
|
|
6908
|
+
odla-ai admin ai credentials [--context <name>] [--json]
|
|
6909
|
+
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
6910
|
+
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
6911
|
+
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
6668
6912
|
odla-ai security github connect [--repo owner/name] [--env dev] [--email <odla-account>] [--no-open]
|
|
6669
6913
|
odla-ai security github disconnect --source <id> [--env dev] [--yes]
|
|
6670
6914
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -7091,8 +7335,8 @@ function digestText(value) {
|
|
|
7091
7335
|
var import_node_child_process6 = require("child_process");
|
|
7092
7336
|
var import_node_crypto2 = require("crypto");
|
|
7093
7337
|
var import_promises10 = require("fs/promises");
|
|
7094
|
-
var
|
|
7095
|
-
var
|
|
7338
|
+
var import_node_os3 = require("os");
|
|
7339
|
+
var import_node_path11 = require("path");
|
|
7096
7340
|
var import_node_url3 = require("url");
|
|
7097
7341
|
|
|
7098
7342
|
// src/code-runtime-config.ts
|
|
@@ -7178,10 +7422,10 @@ async function embeddedPiImageName() {
|
|
|
7178
7422
|
return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto2.createHash)("sha256").update(bundle).digest("hex")}`;
|
|
7179
7423
|
}
|
|
7180
7424
|
async function buildEmbeddedPiImage(engine, image, run) {
|
|
7181
|
-
const context = await (0, import_promises10.mkdtemp)((0,
|
|
7425
|
+
const context = await (0, import_promises10.mkdtemp)((0, import_node_path11.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
|
|
7182
7426
|
try {
|
|
7183
|
-
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0,
|
|
7184
|
-
await (0, import_promises10.writeFile)((0,
|
|
7427
|
+
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path11.join)(context, "pi-agent.js"));
|
|
7428
|
+
await (0, import_promises10.writeFile)((0, import_node_path11.join)(context, "Dockerfile"), [
|
|
7185
7429
|
`FROM ${CODE_NODE_IMAGE}`,
|
|
7186
7430
|
"COPY pi-agent.js /opt/odla/pi-agent.js",
|
|
7187
7431
|
"WORKDIR /workspace",
|
|
@@ -7197,8 +7441,8 @@ async function buildEmbeddedPiImage(engine, image, run) {
|
|
|
7197
7441
|
// src/code-connect.ts
|
|
7198
7442
|
async function codeConnect(options) {
|
|
7199
7443
|
const cwd = options.cwd ?? process.cwd();
|
|
7200
|
-
const configPath = (0,
|
|
7201
|
-
const cfg = (0,
|
|
7444
|
+
const configPath = (0, import_node_path12.resolve)(cwd, options.configPath);
|
|
7445
|
+
const cfg = (0, import_node_fs14.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
7202
7446
|
const requestedAppId = options.appId?.trim();
|
|
7203
7447
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
7204
7448
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -7232,7 +7476,7 @@ async function codeConnect(options) {
|
|
|
7232
7476
|
);
|
|
7233
7477
|
if (!piImage || !/^odla-ai\/pi-agent:embedded-sha256-[0-9a-f]{64}$/.test(piImage)) throw new Error("Code image preflight did not produce the content-addressed embedded Pi runtime");
|
|
7234
7478
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
7235
|
-
const hostName = (options.name ?? (0,
|
|
7479
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
7236
7480
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
7237
7481
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
7238
7482
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -7269,8 +7513,8 @@ async function codeConnect(options) {
|
|
|
7269
7513
|
platform: hostPlatform,
|
|
7270
7514
|
arch: process.arch,
|
|
7271
7515
|
engines: [engine],
|
|
7272
|
-
cpuCount: (0,
|
|
7273
|
-
memoryBytes: (0,
|
|
7516
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
7517
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
7274
7518
|
source: descriptor2,
|
|
7275
7519
|
images: {
|
|
7276
7520
|
ready: true,
|
|
@@ -7416,246 +7660,19 @@ async function codeCommand(parsed, dependencies) {
|
|
|
7416
7660
|
}
|
|
7417
7661
|
|
|
7418
7662
|
// src/operator-credentials.ts
|
|
7419
|
-
var
|
|
7663
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
7420
7664
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
7421
7665
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
7422
7666
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
7423
|
-
const source =
|
|
7667
|
+
const source = clean3(
|
|
7424
7668
|
stringOpt(parsed.options.token)
|
|
7425
|
-
) ? "flag" :
|
|
7669
|
+
) ? "flag" : clean3(import_node_process9.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
7426
7670
|
return {
|
|
7427
7671
|
source,
|
|
7428
7672
|
cacheFile: context.cfg.local.tokenFile,
|
|
7429
7673
|
cacheStatus
|
|
7430
7674
|
};
|
|
7431
7675
|
}
|
|
7432
|
-
function clean(value) {
|
|
7433
|
-
const normalized = value?.trim();
|
|
7434
|
-
return normalized || void 0;
|
|
7435
|
-
}
|
|
7436
|
-
|
|
7437
|
-
// src/operator-context.ts
|
|
7438
|
-
var import_node_fs14 = require("fs");
|
|
7439
|
-
var import_node_path12 = require("path");
|
|
7440
|
-
var import_node_process9 = __toESM(require("process"), 1);
|
|
7441
|
-
|
|
7442
|
-
// src/operator-profiles.ts
|
|
7443
|
-
var import_node_fs13 = require("fs");
|
|
7444
|
-
var import_node_os4 = require("os");
|
|
7445
|
-
var import_node_path11 = require("path");
|
|
7446
|
-
var import_node_process8 = __toESM(require("process"), 1);
|
|
7447
|
-
function operatorProfileFile() {
|
|
7448
|
-
return (0, import_node_path11.resolve)(
|
|
7449
|
-
clean2(import_node_process8.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".odla", "contexts.json")
|
|
7450
|
-
);
|
|
7451
|
-
}
|
|
7452
|
-
function resolveOperatorProfile(parsed) {
|
|
7453
|
-
const fromFlag = clean2(stringOpt(parsed.options.context));
|
|
7454
|
-
const fromEnvironment = clean2(import_node_process8.default.env.ODLA_CONTEXT);
|
|
7455
|
-
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7456
|
-
const file = operatorProfileFile();
|
|
7457
|
-
if (!name) {
|
|
7458
|
-
return { name: null, source: "unresolved", file, value: null };
|
|
7459
|
-
}
|
|
7460
|
-
assertOperatorName(name, "context");
|
|
7461
|
-
const profiles = readOperatorProfiles(file);
|
|
7462
|
-
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
7463
|
-
if (!value) {
|
|
7464
|
-
throw new Error(
|
|
7465
|
-
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
7466
|
-
);
|
|
7467
|
-
}
|
|
7468
|
-
return {
|
|
7469
|
-
name,
|
|
7470
|
-
source: fromFlag ? "flag" : "environment",
|
|
7471
|
-
file,
|
|
7472
|
-
value
|
|
7473
|
-
};
|
|
7474
|
-
}
|
|
7475
|
-
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
7476
|
-
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
7477
|
-
}
|
|
7478
|
-
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
7479
|
-
assertOperatorName(name, "context");
|
|
7480
|
-
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
7481
|
-
const profiles = readOperatorProfiles(file);
|
|
7482
|
-
profiles[name] = normalized;
|
|
7483
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7484
|
-
}
|
|
7485
|
-
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
7486
|
-
assertOperatorName(name, "context");
|
|
7487
|
-
const profiles = readOperatorProfiles(file);
|
|
7488
|
-
if (!Object.hasOwn(profiles, name)) return false;
|
|
7489
|
-
delete profiles[name];
|
|
7490
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7491
|
-
return true;
|
|
7492
|
-
}
|
|
7493
|
-
function operatorCredentialFiles(selection) {
|
|
7494
|
-
const base = selection.name ? (0, import_node_path11.join)((0, import_node_path11.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".odla");
|
|
7495
|
-
return {
|
|
7496
|
-
developer: (0, import_node_path11.join)(base, "dev-token.json"),
|
|
7497
|
-
scoped: (0, import_node_path11.join)(base, "admin-token.local.json")
|
|
7498
|
-
};
|
|
7499
|
-
}
|
|
7500
|
-
function assertOperatorName(value, label) {
|
|
7501
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
7502
|
-
throw new Error(
|
|
7503
|
-
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
7504
|
-
);
|
|
7505
|
-
}
|
|
7506
|
-
}
|
|
7507
|
-
function readOperatorProfiles(file) {
|
|
7508
|
-
if (!(0, import_node_fs13.existsSync)(file)) return emptyProfiles();
|
|
7509
|
-
let raw;
|
|
7510
|
-
try {
|
|
7511
|
-
raw = JSON.parse((0, import_node_fs13.readFileSync)(file, "utf8"));
|
|
7512
|
-
} catch {
|
|
7513
|
-
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
7514
|
-
}
|
|
7515
|
-
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
7516
|
-
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
7517
|
-
}
|
|
7518
|
-
const unknown = Object.keys(raw).filter(
|
|
7519
|
-
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
7520
|
-
);
|
|
7521
|
-
if (unknown.length > 0) {
|
|
7522
|
-
throw new Error(
|
|
7523
|
-
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
7524
|
-
);
|
|
7525
|
-
}
|
|
7526
|
-
const profiles = emptyProfiles();
|
|
7527
|
-
for (const [name, value] of Object.entries(
|
|
7528
|
-
raw.profiles
|
|
7529
|
-
)) {
|
|
7530
|
-
assertOperatorName(name, "context");
|
|
7531
|
-
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
7532
|
-
}
|
|
7533
|
-
return profiles;
|
|
7534
|
-
}
|
|
7535
|
-
function emptyProfiles() {
|
|
7536
|
-
return /* @__PURE__ */ Object.create(null);
|
|
7537
|
-
}
|
|
7538
|
-
function validateProfile(value, label) {
|
|
7539
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7540
|
-
throw new Error(`${label} has an invalid shape`);
|
|
7541
|
-
}
|
|
7542
|
-
const unknown = Object.keys(value).filter(
|
|
7543
|
-
(key) => !["platform", "app", "environment"].includes(key)
|
|
7544
|
-
);
|
|
7545
|
-
if (unknown.length > 0) {
|
|
7546
|
-
throw new Error(
|
|
7547
|
-
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
7548
|
-
);
|
|
7549
|
-
}
|
|
7550
|
-
const candidate = value;
|
|
7551
|
-
if (typeof candidate.platform !== "string") {
|
|
7552
|
-
throw new Error(`${label} needs an absolute platform URL`);
|
|
7553
|
-
}
|
|
7554
|
-
const platform = platformAudience(candidate.platform);
|
|
7555
|
-
const app = clean2(candidate.app);
|
|
7556
|
-
const environment2 = clean2(candidate.environment);
|
|
7557
|
-
if (app) assertOperatorName(app, "app");
|
|
7558
|
-
if (environment2) assertOperatorName(environment2, "environment");
|
|
7559
|
-
return {
|
|
7560
|
-
platform,
|
|
7561
|
-
...app ? { app } : {},
|
|
7562
|
-
...environment2 ? { environment: environment2 } : {}
|
|
7563
|
-
};
|
|
7564
|
-
}
|
|
7565
|
-
function clean2(value) {
|
|
7566
|
-
const normalized = value?.trim();
|
|
7567
|
-
return normalized || void 0;
|
|
7568
|
-
}
|
|
7569
|
-
|
|
7570
|
-
// src/operator-context.ts
|
|
7571
|
-
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
7572
|
-
async function resolveOperatorContext(parsed, options = {}) {
|
|
7573
|
-
const profile = resolveOperatorProfile(parsed);
|
|
7574
|
-
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
7575
|
-
const configPath = (0, import_node_path12.resolve)(configArgument);
|
|
7576
|
-
const explicitConfig = parsed.options.config !== void 0;
|
|
7577
|
-
const hasConfig = (0, import_node_fs14.existsSync)(configPath);
|
|
7578
|
-
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
7579
|
-
await loadProjectConfig(configArgument);
|
|
7580
|
-
}
|
|
7581
|
-
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
7582
|
-
const platformFlag = clean3(stringOpt(parsed.options.platform));
|
|
7583
|
-
const platformEnvironment = clean3(import_node_process9.default.env.ODLA_PLATFORM_URL);
|
|
7584
|
-
const platformValue = platformAudience(
|
|
7585
|
-
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
7586
|
-
);
|
|
7587
|
-
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
7588
|
-
const appFlag = clean3(stringOpt(parsed.options.app));
|
|
7589
|
-
const appEnvironment = clean3(import_node_process9.default.env.ODLA_APP_ID);
|
|
7590
|
-
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
7591
|
-
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
7592
|
-
if (appValue) assertOperatorName(appValue, "app");
|
|
7593
|
-
if (options.requireApp && !appValue) {
|
|
7594
|
-
throw new Error(
|
|
7595
|
-
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
7596
|
-
);
|
|
7597
|
-
}
|
|
7598
|
-
const envFlag = clean3(stringOpt(parsed.options.env));
|
|
7599
|
-
const envEnvironment = clean3(import_node_process9.default.env.ODLA_ENV);
|
|
7600
|
-
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
7601
|
-
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
7602
|
-
if (environmentValue) {
|
|
7603
|
-
assertOperatorName(environmentValue, "environment");
|
|
7604
|
-
}
|
|
7605
|
-
const rootDir = loaded?.rootDir ?? import_node_process9.default.cwd();
|
|
7606
|
-
const profileCredentials = operatorCredentialFiles(profile);
|
|
7607
|
-
const tokenFile = clean3(import_node_process9.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path12.resolve)(import_node_process9.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
7608
|
-
const scopedTokenFile = clean3(import_node_process9.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path12.resolve)(import_node_process9.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path12.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
7609
|
-
const cfg = loaded ? {
|
|
7610
|
-
...loaded,
|
|
7611
|
-
platformUrl: platformValue,
|
|
7612
|
-
app: appValue ? {
|
|
7613
|
-
id: appValue,
|
|
7614
|
-
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
7615
|
-
} : loaded.app,
|
|
7616
|
-
local: { ...loaded.local, tokenFile }
|
|
7617
|
-
} : {
|
|
7618
|
-
configPath,
|
|
7619
|
-
rootDir,
|
|
7620
|
-
platformUrl: platformValue,
|
|
7621
|
-
dbEndpoint: platformValue,
|
|
7622
|
-
app: {
|
|
7623
|
-
id: appValue ?? "operator",
|
|
7624
|
-
name: appValue ?? "Operator"
|
|
7625
|
-
},
|
|
7626
|
-
envs: environmentValue ? [environmentValue] : [],
|
|
7627
|
-
services: [],
|
|
7628
|
-
local: {
|
|
7629
|
-
tokenFile,
|
|
7630
|
-
credentialsFile: (0, import_node_path12.join)(rootDir, ".odla", "credentials.local.json"),
|
|
7631
|
-
devVarsFile: (0, import_node_path12.join)(rootDir, ".dev.vars"),
|
|
7632
|
-
gitignore: true
|
|
7633
|
-
}
|
|
7634
|
-
};
|
|
7635
|
-
return {
|
|
7636
|
-
cfg,
|
|
7637
|
-
profile: {
|
|
7638
|
-
name: profile.name,
|
|
7639
|
-
source: profile.source,
|
|
7640
|
-
file: profile.file
|
|
7641
|
-
},
|
|
7642
|
-
config: {
|
|
7643
|
-
path: configPath,
|
|
7644
|
-
status: loaded ? "loaded" : "absent",
|
|
7645
|
-
explicit: explicitConfig
|
|
7646
|
-
},
|
|
7647
|
-
platform: { value: platformValue, source: platformSource },
|
|
7648
|
-
app: { value: appValue, source: appSource },
|
|
7649
|
-
environment: {
|
|
7650
|
-
value: environmentValue,
|
|
7651
|
-
source: environmentSource
|
|
7652
|
-
},
|
|
7653
|
-
credentials: {
|
|
7654
|
-
developerTokenFile: tokenFile,
|
|
7655
|
-
scopedTokenFile
|
|
7656
|
-
}
|
|
7657
|
-
};
|
|
7658
|
-
}
|
|
7659
7676
|
function clean3(value) {
|
|
7660
7677
|
const normalized = value?.trim();
|
|
7661
7678
|
return normalized || void 0;
|
|
@@ -8499,33 +8516,53 @@ var ALIASES = {
|
|
|
8499
8516
|
decision: "decision",
|
|
8500
8517
|
bug: "bug"
|
|
8501
8518
|
};
|
|
8502
|
-
var
|
|
8503
|
-
|
|
8504
|
-
"
|
|
8505
|
-
"
|
|
8506
|
-
|
|
8507
|
-
"
|
|
8508
|
-
"
|
|
8509
|
-
"
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
]
|
|
8519
|
+
var COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
8520
|
+
var ACTION_OPTIONS = {
|
|
8521
|
+
list: ["app", "q", "limit", "offset"],
|
|
8522
|
+
add: ["app", "title", "mutation-id"],
|
|
8523
|
+
get: [],
|
|
8524
|
+
set: ["mutation-id"],
|
|
8525
|
+
done: ["mutation-id"],
|
|
8526
|
+
comment: ["body", "mutation-id"],
|
|
8527
|
+
comments: [],
|
|
8528
|
+
rm: []
|
|
8529
|
+
};
|
|
8530
|
+
var ENTITY_OPTIONS = {
|
|
8531
|
+
goal: {
|
|
8532
|
+
list: ["status"],
|
|
8533
|
+
add: ["status", "proof", "target"],
|
|
8534
|
+
set: ["title", "status", "proof", "target"],
|
|
8535
|
+
done: []
|
|
8536
|
+
},
|
|
8537
|
+
task: {
|
|
8538
|
+
list: ["column", "goal", "assignee"],
|
|
8539
|
+
add: ["column", "goal", "assignee", "due", "description", "desc", "body"],
|
|
8540
|
+
set: ["title", "column", "rank", "goal", "assignee", "due", "description", "desc", "body"],
|
|
8541
|
+
done: []
|
|
8542
|
+
},
|
|
8543
|
+
decision: {
|
|
8544
|
+
list: ["status"],
|
|
8545
|
+
add: ["status", "body"],
|
|
8546
|
+
set: ["title", "status", "body"],
|
|
8547
|
+
done: []
|
|
8548
|
+
},
|
|
8549
|
+
bug: {
|
|
8550
|
+
list: ["status", "severity", "goal", "assignee", "decision"],
|
|
8551
|
+
add: ["status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
8552
|
+
set: ["title", "status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
8553
|
+
done: ["decision"]
|
|
8554
|
+
}
|
|
8555
|
+
};
|
|
8556
|
+
function canonicalAction(action) {
|
|
8557
|
+
if (action === "create") return "add";
|
|
8558
|
+
if (action === "update" || action === "status" || action === "move") return "set";
|
|
8559
|
+
if (action === "delete") return "rm";
|
|
8560
|
+
return action in ACTION_OPTIONS ? action : null;
|
|
8561
|
+
}
|
|
8562
|
+
function allowedOptions(entity, action) {
|
|
8563
|
+
const entityOptions = action === "list" || action === "add" || action === "set" || action === "done" ? ENTITY_OPTIONS[entity][action] : [];
|
|
8564
|
+
return [...COMMON_OPTIONS, ...ACTION_OPTIONS[action], ...entityOptions];
|
|
8565
|
+
}
|
|
8529
8566
|
function requireId2(id, action) {
|
|
8530
8567
|
if (!id) throw new Error(`"pm ... ${action}" needs an item id`);
|
|
8531
8568
|
return id;
|
|
@@ -8557,27 +8594,25 @@ async function buildContext2(parsed, deps) {
|
|
|
8557
8594
|
async function pmCommand(parsed, deps = {}) {
|
|
8558
8595
|
const word = parsed.positionals[1] ?? "";
|
|
8559
8596
|
if (word === "handoff") {
|
|
8560
|
-
assertArgs(parsed,
|
|
8597
|
+
assertArgs(parsed, [...COMMON_OPTIONS, "app"], 2);
|
|
8561
8598
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
8562
8599
|
}
|
|
8563
8600
|
const entity = ALIASES[word];
|
|
8564
8601
|
if (!entity) throw new Error(`unknown pm entity "${word}". Try "odla-ai pm bug list" (goal|task|decision|bug).`);
|
|
8565
|
-
const
|
|
8566
|
-
|
|
8602
|
+
const requestedAction = parsed.positionals[2] ?? "list";
|
|
8603
|
+
const action = canonicalAction(requestedAction);
|
|
8604
|
+
if (!action) throw new Error(`unknown pm action "${requestedAction}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
8605
|
+
assertArgs(parsed, allowedOptions(entity, action), 4);
|
|
8567
8606
|
const ctx = await buildContext2(parsed, deps);
|
|
8568
8607
|
const id = parsed.positionals[3];
|
|
8569
8608
|
switch (action) {
|
|
8570
8609
|
case "list":
|
|
8571
8610
|
return pmList(ctx, entity, parsed);
|
|
8572
8611
|
case "add":
|
|
8573
|
-
case "create":
|
|
8574
8612
|
return pmAdd(ctx, entity, parsed);
|
|
8575
8613
|
case "get":
|
|
8576
8614
|
return pmGet(ctx, entity, requireId2(id, action));
|
|
8577
8615
|
case "set":
|
|
8578
|
-
case "update":
|
|
8579
|
-
case "status":
|
|
8580
|
-
case "move":
|
|
8581
8616
|
return pmSet(ctx, entity, requireId2(id, action), parsed);
|
|
8582
8617
|
case "done":
|
|
8583
8618
|
return pmDone(ctx, entity, requireId2(id, action), parsed);
|
|
@@ -8586,10 +8621,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
8586
8621
|
case "comments":
|
|
8587
8622
|
return pmComments(ctx, entity, requireId2(id, action));
|
|
8588
8623
|
case "rm":
|
|
8589
|
-
case "delete":
|
|
8590
8624
|
return pmRemove(ctx, entity, requireId2(id, action));
|
|
8591
|
-
default:
|
|
8592
|
-
throw new Error(`unknown pm action "${action}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
8593
8625
|
}
|
|
8594
8626
|
}
|
|
8595
8627
|
|
|
@@ -9560,7 +9592,7 @@ function recordInvocation(parsed) {
|
|
|
9560
9592
|
try {
|
|
9561
9593
|
const entry = {
|
|
9562
9594
|
path: invocationPath(parsed.positionals),
|
|
9563
|
-
options: Object.
|
|
9595
|
+
options: Object.entries(parsed.options).map(([name, value]) => value === false ? `no-${name}` : name).sort()
|
|
9564
9596
|
};
|
|
9565
9597
|
if (!entry.path.length) return;
|
|
9566
9598
|
(0, import_node_fs15.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
@@ -10333,7 +10365,7 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
10333
10365
|
}
|
|
10334
10366
|
|
|
10335
10367
|
// src/runbook-command.ts
|
|
10336
|
-
var
|
|
10368
|
+
var ALLOWED2 = [
|
|
10337
10369
|
"config",
|
|
10338
10370
|
"token",
|
|
10339
10371
|
"email",
|
|
@@ -10421,7 +10453,7 @@ async function buildContext3(parsed, deps, action) {
|
|
|
10421
10453
|
}
|
|
10422
10454
|
async function runbookCommand(parsed, deps = {}) {
|
|
10423
10455
|
const action = parsed.positionals[1] ?? "list";
|
|
10424
|
-
assertArgs(parsed,
|
|
10456
|
+
assertArgs(parsed, ALLOWED2, action === "ask" || action === "search" ? 64 : 4);
|
|
10425
10457
|
const ctx = await buildContext3(parsed, deps, action);
|
|
10426
10458
|
const slug = parsed.positionals[2];
|
|
10427
10459
|
switch (action) {
|
|
@@ -11261,7 +11293,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
11261
11293
|
return;
|
|
11262
11294
|
}
|
|
11263
11295
|
if (command === "admin") {
|
|
11264
|
-
await adminCommand(parsed);
|
|
11296
|
+
await adminCommand(parsed, runtime);
|
|
11265
11297
|
return;
|
|
11266
11298
|
}
|
|
11267
11299
|
if (command === "agent") {
|