@odla-ai/cli 0.25.18 → 0.25.20
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 +16 -8
- package/dist/bin.cjs +428 -405
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-B6DISJPC.js → chunk-ONETPJFW.js} +305 -282
- package/dist/chunk-ONETPJFW.js.map +1 -0
- package/dist/index.cjs +428 -405
- 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 +2 -2
- package/dist/chunk-B6DISJPC.js.map +0 -1
package/dist/bin.cjs
CHANGED
|
@@ -959,71 +959,16 @@ function addOption(options, name, value) {
|
|
|
959
959
|
else options[name] = [String(current), String(value)];
|
|
960
960
|
}
|
|
961
961
|
|
|
962
|
-
// src/
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
967
|
-
const credentials = action === "credentials";
|
|
968
|
-
const models = action === "models";
|
|
969
|
-
const usage = action === "usage";
|
|
970
|
-
const audit = action === "audit";
|
|
971
|
-
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
972
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
973
|
-
}
|
|
974
|
-
assertArgs(parsed, [
|
|
975
|
-
"platform",
|
|
976
|
-
"json",
|
|
977
|
-
"open",
|
|
978
|
-
"email",
|
|
979
|
-
"provider",
|
|
980
|
-
"model",
|
|
981
|
-
"enabled",
|
|
982
|
-
"max-input-bytes",
|
|
983
|
-
"max-output-tokens",
|
|
984
|
-
"max-calls-per-run",
|
|
985
|
-
"from-env",
|
|
986
|
-
"stdin",
|
|
987
|
-
"discovery-provider",
|
|
988
|
-
"discovery-model",
|
|
989
|
-
"validation-provider",
|
|
990
|
-
"validation-model",
|
|
991
|
-
"app-id",
|
|
992
|
-
"env",
|
|
993
|
-
"run-id",
|
|
994
|
-
"limit"
|
|
995
|
-
], credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
996
|
-
await adminAi({
|
|
997
|
-
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
998
|
-
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
999
|
-
provider: stringOpt(parsed.options.provider),
|
|
1000
|
-
model: stringOpt(parsed.options.model),
|
|
1001
|
-
enabled: boolOpt(parsed.options.enabled),
|
|
1002
|
-
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
1003
|
-
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
1004
|
-
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
1005
|
-
platform: stringOpt(parsed.options.platform),
|
|
1006
|
-
email: stringOpt(parsed.options.email),
|
|
1007
|
-
json: parsed.options.json === true,
|
|
1008
|
-
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1009
|
-
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
1010
|
-
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
1011
|
-
stdin: parsed.options.stdin === true,
|
|
1012
|
-
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
1013
|
-
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
1014
|
-
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
1015
|
-
validationModel: stringOpt(parsed.options["validation-model"]),
|
|
1016
|
-
appId: stringOpt(parsed.options["app-id"]),
|
|
1017
|
-
env: stringOpt(parsed.options.env),
|
|
1018
|
-
runId: stringOpt(parsed.options["run-id"]),
|
|
1019
|
-
limit: numberOpt(parsed.options.limit, "--limit")
|
|
1020
|
-
});
|
|
1021
|
-
}
|
|
962
|
+
// src/operator-context.ts
|
|
963
|
+
var import_node_fs6 = require("fs");
|
|
964
|
+
var import_node_path6 = require("path");
|
|
965
|
+
var import_node_process8 = __toESM(require("process"), 1);
|
|
1022
966
|
|
|
1023
967
|
// src/config.ts
|
|
1024
968
|
var import_node_fs4 = require("fs");
|
|
1025
969
|
var import_node_path4 = require("path");
|
|
1026
970
|
var import_node_url = require("url");
|
|
971
|
+
var import_apps = require("@odla-ai/apps");
|
|
1027
972
|
|
|
1028
973
|
// src/integration-validation.ts
|
|
1029
974
|
function validateIntegrations(cfg, path, defaultServices) {
|
|
@@ -1111,6 +1056,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs") {
|
|
|
1111
1056
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
1112
1057
|
const envs = unique2(raw.envs?.length ? raw.envs : DEFAULT_ENVS);
|
|
1113
1058
|
const services = unique2(raw.services?.length ? raw.services : DEFAULT_SERVICES);
|
|
1059
|
+
validateServices(services, resolved);
|
|
1114
1060
|
validateCalendarConfig(raw, envs, services, resolved);
|
|
1115
1061
|
const local = {
|
|
1116
1062
|
tokenFile: (0, import_node_path4.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
@@ -1266,7 +1212,19 @@ function validateCalendarConfig(cfg, envs, services, path) {
|
|
|
1266
1212
|
}
|
|
1267
1213
|
}
|
|
1268
1214
|
}
|
|
1269
|
-
|
|
1215
|
+
}
|
|
1216
|
+
function validateServices(services, path) {
|
|
1217
|
+
for (const service of services) {
|
|
1218
|
+
const definition = (0, import_apps.appServiceDefinition)(service);
|
|
1219
|
+
if (!definition) {
|
|
1220
|
+
throw new Error(`${path}: unknown service "${service}" (known: ${(0, import_apps.appServiceIds)().join(", ")})`);
|
|
1221
|
+
}
|
|
1222
|
+
for (const dependency of definition.requires) {
|
|
1223
|
+
if (!services.includes(dependency)) {
|
|
1224
|
+
throw new Error(`${path}: ${service} service requires the ${dependency} service`);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1270
1228
|
}
|
|
1271
1229
|
function assertOnly(value, allowed, label) {
|
|
1272
1230
|
const extra = Object.keys(value).find((key) => !allowed.includes(key));
|
|
@@ -1304,8 +1262,293 @@ function unique2(values) {
|
|
|
1304
1262
|
return [...new Set(values.filter(Boolean))];
|
|
1305
1263
|
}
|
|
1306
1264
|
|
|
1265
|
+
// src/operator-profiles.ts
|
|
1266
|
+
var import_node_fs5 = require("fs");
|
|
1267
|
+
var import_node_os = require("os");
|
|
1268
|
+
var import_node_path5 = require("path");
|
|
1269
|
+
var import_node_process7 = __toESM(require("process"), 1);
|
|
1270
|
+
function operatorProfileFile() {
|
|
1271
|
+
return (0, import_node_path5.resolve)(
|
|
1272
|
+
clean(import_node_process7.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path5.join)((0, import_node_os.homedir)(), ".odla", "contexts.json")
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
function resolveOperatorProfile(parsed) {
|
|
1276
|
+
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1277
|
+
const fromEnvironment = clean(import_node_process7.default.env.ODLA_CONTEXT);
|
|
1278
|
+
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1279
|
+
const file = operatorProfileFile();
|
|
1280
|
+
if (!name) {
|
|
1281
|
+
return { name: null, source: "unresolved", file, value: null };
|
|
1282
|
+
}
|
|
1283
|
+
assertOperatorName(name, "context");
|
|
1284
|
+
const profiles = readOperatorProfiles(file);
|
|
1285
|
+
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
1286
|
+
if (!value) {
|
|
1287
|
+
throw new Error(
|
|
1288
|
+
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
name,
|
|
1293
|
+
source: fromFlag ? "flag" : "environment",
|
|
1294
|
+
file,
|
|
1295
|
+
value
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
1299
|
+
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
1300
|
+
}
|
|
1301
|
+
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
1302
|
+
assertOperatorName(name, "context");
|
|
1303
|
+
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
1304
|
+
const profiles = readOperatorProfiles(file);
|
|
1305
|
+
profiles[name] = normalized;
|
|
1306
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1307
|
+
}
|
|
1308
|
+
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
1309
|
+
assertOperatorName(name, "context");
|
|
1310
|
+
const profiles = readOperatorProfiles(file);
|
|
1311
|
+
if (!Object.hasOwn(profiles, name)) return false;
|
|
1312
|
+
delete profiles[name];
|
|
1313
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1314
|
+
return true;
|
|
1315
|
+
}
|
|
1316
|
+
function operatorCredentialFiles(selection) {
|
|
1317
|
+
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");
|
|
1318
|
+
return {
|
|
1319
|
+
developer: (0, import_node_path5.join)(base, "dev-token.json"),
|
|
1320
|
+
scoped: (0, import_node_path5.join)(base, "admin-token.local.json")
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function assertOperatorName(value, label) {
|
|
1324
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
1325
|
+
throw new Error(
|
|
1326
|
+
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function readOperatorProfiles(file) {
|
|
1331
|
+
if (!(0, import_node_fs5.existsSync)(file)) return emptyProfiles();
|
|
1332
|
+
let raw;
|
|
1333
|
+
try {
|
|
1334
|
+
raw = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
|
|
1335
|
+
} catch {
|
|
1336
|
+
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1337
|
+
}
|
|
1338
|
+
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
1339
|
+
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
1340
|
+
}
|
|
1341
|
+
const unknown = Object.keys(raw).filter(
|
|
1342
|
+
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
1343
|
+
);
|
|
1344
|
+
if (unknown.length > 0) {
|
|
1345
|
+
throw new Error(
|
|
1346
|
+
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
const profiles = emptyProfiles();
|
|
1350
|
+
for (const [name, value] of Object.entries(
|
|
1351
|
+
raw.profiles
|
|
1352
|
+
)) {
|
|
1353
|
+
assertOperatorName(name, "context");
|
|
1354
|
+
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
1355
|
+
}
|
|
1356
|
+
return profiles;
|
|
1357
|
+
}
|
|
1358
|
+
function emptyProfiles() {
|
|
1359
|
+
return /* @__PURE__ */ Object.create(null);
|
|
1360
|
+
}
|
|
1361
|
+
function validateProfile(value, label) {
|
|
1362
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1363
|
+
throw new Error(`${label} has an invalid shape`);
|
|
1364
|
+
}
|
|
1365
|
+
const unknown = Object.keys(value).filter(
|
|
1366
|
+
(key) => !["platform", "app", "environment"].includes(key)
|
|
1367
|
+
);
|
|
1368
|
+
if (unknown.length > 0) {
|
|
1369
|
+
throw new Error(
|
|
1370
|
+
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
const candidate = value;
|
|
1374
|
+
if (typeof candidate.platform !== "string") {
|
|
1375
|
+
throw new Error(`${label} needs an absolute platform URL`);
|
|
1376
|
+
}
|
|
1377
|
+
const platform = platformAudience(candidate.platform);
|
|
1378
|
+
const app = clean(candidate.app);
|
|
1379
|
+
const environment2 = clean(candidate.environment);
|
|
1380
|
+
if (app) assertOperatorName(app, "app");
|
|
1381
|
+
if (environment2) assertOperatorName(environment2, "environment");
|
|
1382
|
+
return {
|
|
1383
|
+
platform,
|
|
1384
|
+
...app ? { app } : {},
|
|
1385
|
+
...environment2 ? { environment: environment2 } : {}
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
function clean(value) {
|
|
1389
|
+
const normalized = value?.trim();
|
|
1390
|
+
return normalized || void 0;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// src/operator-context.ts
|
|
1394
|
+
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
1395
|
+
async function resolveOperatorContext(parsed, options = {}) {
|
|
1396
|
+
const profile = resolveOperatorProfile(parsed);
|
|
1397
|
+
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1398
|
+
const configPath = (0, import_node_path6.resolve)(configArgument);
|
|
1399
|
+
const explicitConfig = parsed.options.config !== void 0;
|
|
1400
|
+
const hasConfig = (0, import_node_fs6.existsSync)(configPath);
|
|
1401
|
+
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1402
|
+
await loadProjectConfig(configArgument);
|
|
1403
|
+
}
|
|
1404
|
+
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1405
|
+
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1406
|
+
const platformEnvironment = clean2(import_node_process8.default.env.ODLA_PLATFORM_URL);
|
|
1407
|
+
const platformValue = platformAudience(
|
|
1408
|
+
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1409
|
+
);
|
|
1410
|
+
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1411
|
+
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1412
|
+
const appEnvironment = clean2(import_node_process8.default.env.ODLA_APP_ID);
|
|
1413
|
+
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1414
|
+
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1415
|
+
if (appValue) assertOperatorName(appValue, "app");
|
|
1416
|
+
if (options.requireApp && !appValue) {
|
|
1417
|
+
throw new Error(
|
|
1418
|
+
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1422
|
+
const envEnvironment = clean2(import_node_process8.default.env.ODLA_ENV);
|
|
1423
|
+
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1424
|
+
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1425
|
+
if (environmentValue) {
|
|
1426
|
+
assertOperatorName(environmentValue, "environment");
|
|
1427
|
+
}
|
|
1428
|
+
const rootDir = loaded?.rootDir ?? import_node_process8.default.cwd();
|
|
1429
|
+
const profileCredentials = operatorCredentialFiles(profile);
|
|
1430
|
+
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;
|
|
1431
|
+
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;
|
|
1432
|
+
const cfg = loaded ? {
|
|
1433
|
+
...loaded,
|
|
1434
|
+
platformUrl: platformValue,
|
|
1435
|
+
app: appValue ? {
|
|
1436
|
+
id: appValue,
|
|
1437
|
+
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
1438
|
+
} : loaded.app,
|
|
1439
|
+
local: { ...loaded.local, tokenFile }
|
|
1440
|
+
} : {
|
|
1441
|
+
configPath,
|
|
1442
|
+
rootDir,
|
|
1443
|
+
platformUrl: platformValue,
|
|
1444
|
+
dbEndpoint: platformValue,
|
|
1445
|
+
app: {
|
|
1446
|
+
id: appValue ?? "operator",
|
|
1447
|
+
name: appValue ?? "Operator"
|
|
1448
|
+
},
|
|
1449
|
+
envs: environmentValue ? [environmentValue] : [],
|
|
1450
|
+
services: [],
|
|
1451
|
+
local: {
|
|
1452
|
+
tokenFile,
|
|
1453
|
+
credentialsFile: (0, import_node_path6.join)(rootDir, ".odla", "credentials.local.json"),
|
|
1454
|
+
devVarsFile: (0, import_node_path6.join)(rootDir, ".dev.vars"),
|
|
1455
|
+
gitignore: true
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
return {
|
|
1459
|
+
cfg,
|
|
1460
|
+
profile: {
|
|
1461
|
+
name: profile.name,
|
|
1462
|
+
source: profile.source,
|
|
1463
|
+
file: profile.file
|
|
1464
|
+
},
|
|
1465
|
+
config: {
|
|
1466
|
+
path: configPath,
|
|
1467
|
+
status: loaded ? "loaded" : "absent",
|
|
1468
|
+
explicit: explicitConfig
|
|
1469
|
+
},
|
|
1470
|
+
platform: { value: platformValue, source: platformSource },
|
|
1471
|
+
app: { value: appValue, source: appSource },
|
|
1472
|
+
environment: {
|
|
1473
|
+
value: environmentValue,
|
|
1474
|
+
source: environmentSource
|
|
1475
|
+
},
|
|
1476
|
+
credentials: {
|
|
1477
|
+
developerTokenFile: tokenFile,
|
|
1478
|
+
scopedTokenFile
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
function clean2(value) {
|
|
1483
|
+
const normalized = value?.trim();
|
|
1484
|
+
return normalized || void 0;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// src/admin-command.ts
|
|
1488
|
+
var CONTEXT_OPTIONS = ["platform", "config", "context", "token", "open", "email"];
|
|
1489
|
+
var JSON_OPTIONS = [...CONTEXT_OPTIONS, "json"];
|
|
1490
|
+
var SET_OPTIONS = [
|
|
1491
|
+
...JSON_OPTIONS,
|
|
1492
|
+
"provider",
|
|
1493
|
+
"model",
|
|
1494
|
+
"enabled",
|
|
1495
|
+
"max-input-bytes",
|
|
1496
|
+
"max-output-tokens",
|
|
1497
|
+
"max-calls-per-run",
|
|
1498
|
+
"discovery-provider",
|
|
1499
|
+
"discovery-model",
|
|
1500
|
+
"validation-provider",
|
|
1501
|
+
"validation-model"
|
|
1502
|
+
];
|
|
1503
|
+
async function adminCommand(parsed, deps = {}) {
|
|
1504
|
+
const area = parsed.positionals[1];
|
|
1505
|
+
const action = parsed.positionals[2];
|
|
1506
|
+
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
1507
|
+
const credentials = action === "credentials";
|
|
1508
|
+
const models = action === "models";
|
|
1509
|
+
const usage = action === "usage";
|
|
1510
|
+
const audit = action === "audit";
|
|
1511
|
+
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
1512
|
+
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
1513
|
+
}
|
|
1514
|
+
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;
|
|
1515
|
+
assertArgs(parsed, allowed, credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
1516
|
+
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
1517
|
+
await adminAi({
|
|
1518
|
+
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
1519
|
+
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
1520
|
+
provider: stringOpt(parsed.options.provider),
|
|
1521
|
+
model: stringOpt(parsed.options.model),
|
|
1522
|
+
enabled: boolOpt(parsed.options.enabled),
|
|
1523
|
+
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
1524
|
+
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
1525
|
+
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
1526
|
+
platform: context.platform.value,
|
|
1527
|
+
token: stringOpt(parsed.options.token),
|
|
1528
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
1529
|
+
email: stringOpt(parsed.options.email),
|
|
1530
|
+
json: parsed.options.json === true,
|
|
1531
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1532
|
+
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
1533
|
+
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
1534
|
+
stdin: parsed.options.stdin === true,
|
|
1535
|
+
readStdin: deps.readStdin,
|
|
1536
|
+
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
1537
|
+
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
1538
|
+
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
1539
|
+
validationModel: stringOpt(parsed.options["validation-model"]),
|
|
1540
|
+
appId: stringOpt(parsed.options["app-id"]),
|
|
1541
|
+
env: stringOpt(parsed.options.env),
|
|
1542
|
+
runId: stringOpt(parsed.options["run-id"]),
|
|
1543
|
+
limit: numberOpt(parsed.options.limit, "--limit"),
|
|
1544
|
+
fetch: deps.fetch,
|
|
1545
|
+
stdout: deps.stdout,
|
|
1546
|
+
openApprovalUrl: deps.openUrl
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1307
1550
|
// src/tenant.ts
|
|
1308
|
-
var
|
|
1551
|
+
var import_apps2 = require("@odla-ai/apps");
|
|
1309
1552
|
function resolveEnv(cfg, requested) {
|
|
1310
1553
|
const env = requested ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
1311
1554
|
if (!env || !cfg.envs.includes(env)) {
|
|
@@ -1315,7 +1558,7 @@ function resolveEnv(cfg, requested) {
|
|
|
1315
1558
|
}
|
|
1316
1559
|
function resolveTenant(cfg, requested) {
|
|
1317
1560
|
const env = resolveEnv(cfg, requested);
|
|
1318
|
-
return { env, tenant: (0,
|
|
1561
|
+
return { env, tenant: (0, import_apps2.tenantIdFor)(cfg.app.id, env) };
|
|
1319
1562
|
}
|
|
1320
1563
|
function bothTenants(cfg) {
|
|
1321
1564
|
for (const env of ["dev", "prod"]) {
|
|
@@ -1325,7 +1568,7 @@ function bothTenants(cfg) {
|
|
|
1325
1568
|
);
|
|
1326
1569
|
}
|
|
1327
1570
|
}
|
|
1328
|
-
return { sandbox: (0,
|
|
1571
|
+
return { sandbox: (0, import_apps2.tenantIdFor)(cfg.app.id, "dev"), live: (0, import_apps2.tenantIdFor)(cfg.app.id, "prod") };
|
|
1329
1572
|
}
|
|
1330
1573
|
|
|
1331
1574
|
// src/agent-command.ts
|
|
@@ -1409,7 +1652,7 @@ function errorMessage(body) {
|
|
|
1409
1652
|
}
|
|
1410
1653
|
|
|
1411
1654
|
// src/app-export.ts
|
|
1412
|
-
var
|
|
1655
|
+
var import_node_fs7 = require("fs");
|
|
1413
1656
|
var import_node_stream = require("stream");
|
|
1414
1657
|
var import_promises = require("stream/promises");
|
|
1415
1658
|
async function appExport(options) {
|
|
@@ -1442,7 +1685,7 @@ async function appExport(options) {
|
|
|
1442
1685
|
const download = await doFetch(`${base}/backups/${newest.id}/download`, { headers: auth });
|
|
1443
1686
|
if (!download.ok || !download.body) throw new Error(`download failed (${download.status})`);
|
|
1444
1687
|
const file = options.out ?? `${tenant}-${new Date(newest.created_at).toISOString().slice(0, 10)}-tx${newest.max_tx}.jsonl.gz`;
|
|
1445
|
-
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(download.body), (0,
|
|
1688
|
+
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(download.body), (0, import_node_fs7.createWriteStream)(file));
|
|
1446
1689
|
if (options.json) out.log(JSON.stringify({ file, backup: newest }, null, 2));
|
|
1447
1690
|
else {
|
|
1448
1691
|
out.log(`${tenant}: wrote ${file} (${newest.bytes} bytes, ${newest.kind} snapshot at tx ${newest.max_tx})`);
|
|
@@ -1452,7 +1695,7 @@ async function appExport(options) {
|
|
|
1452
1695
|
}
|
|
1453
1696
|
|
|
1454
1697
|
// src/app-import.ts
|
|
1455
|
-
var
|
|
1698
|
+
var import_node_fs8 = require("fs");
|
|
1456
1699
|
var import_import = require("@odla-ai/db/import");
|
|
1457
1700
|
function chooseIdMode(options, rows) {
|
|
1458
1701
|
const chosen = [options.idField && "field", options.key && "key", options.generateIds && "generate"].filter(Boolean);
|
|
@@ -1471,7 +1714,7 @@ async function appImport(options) {
|
|
|
1471
1714
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
1472
1715
|
const doFetch = options.fetch ?? fetch;
|
|
1473
1716
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
1474
|
-
const text = options.file === "-" ? (options.readStdin ?? (() => (0,
|
|
1717
|
+
const text = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs8.readFileSync)(0, "utf8")))() : (0, import_node_fs8.readFileSync)(options.file, "utf8");
|
|
1475
1718
|
const { format, sources } = (0, import_import.parseImport)(text, options.ns);
|
|
1476
1719
|
if (format === "namespace-map" && options.ns) {
|
|
1477
1720
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2438,13 +2681,13 @@ function printGroup(out, heading, items) {
|
|
|
2438
2681
|
|
|
2439
2682
|
// src/doctor-checks.ts
|
|
2440
2683
|
var import_node_child_process3 = require("child_process");
|
|
2441
|
-
var
|
|
2442
|
-
var
|
|
2684
|
+
var import_node_fs10 = require("fs");
|
|
2685
|
+
var import_node_path8 = require("path");
|
|
2443
2686
|
|
|
2444
2687
|
// src/wrangler.ts
|
|
2445
2688
|
var import_node_child_process2 = require("child_process");
|
|
2446
|
-
var
|
|
2447
|
-
var
|
|
2689
|
+
var import_node_fs9 = require("fs");
|
|
2690
|
+
var import_node_path7 = require("path");
|
|
2448
2691
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
2449
2692
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
2450
2693
|
let stdout = "";
|
|
@@ -2458,15 +2701,15 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
2458
2701
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
2459
2702
|
function findWranglerConfig(rootDir) {
|
|
2460
2703
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
2461
|
-
const path = (0,
|
|
2462
|
-
if ((0,
|
|
2704
|
+
const path = (0, import_node_path7.join)(rootDir, name);
|
|
2705
|
+
if ((0, import_node_fs9.existsSync)(path)) return path;
|
|
2463
2706
|
}
|
|
2464
2707
|
return null;
|
|
2465
2708
|
}
|
|
2466
2709
|
function readWranglerConfig(path) {
|
|
2467
2710
|
if (path.endsWith(".toml")) return null;
|
|
2468
2711
|
try {
|
|
2469
|
-
return JSON.parse(stripJsonComments((0,
|
|
2712
|
+
return JSON.parse(stripJsonComments((0, import_node_fs9.readFileSync)(path, "utf8")));
|
|
2470
2713
|
} catch {
|
|
2471
2714
|
return null;
|
|
2472
2715
|
}
|
|
@@ -2564,10 +2807,10 @@ function wranglerWarnings(rootDir) {
|
|
|
2564
2807
|
for (const { label, block } of blocks) {
|
|
2565
2808
|
const assets = block.assets;
|
|
2566
2809
|
if (assets?.directory) {
|
|
2567
|
-
const dir = (0,
|
|
2568
|
-
if (dir === (0,
|
|
2810
|
+
const dir = (0, import_node_path8.resolve)(rootDir, assets.directory);
|
|
2811
|
+
if (dir === (0, import_node_path8.resolve)(rootDir)) {
|
|
2569
2812
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
2570
|
-
} else if ((0,
|
|
2813
|
+
} else if ((0, import_node_fs10.existsSync)((0, import_node_path8.join)(dir, "node_modules"))) {
|
|
2571
2814
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
2572
2815
|
}
|
|
2573
2816
|
}
|
|
@@ -2602,13 +2845,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
2602
2845
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
2603
2846
|
return warnings;
|
|
2604
2847
|
}
|
|
2605
|
-
const main = typeof config.main === "string" ? (0,
|
|
2606
|
-
if (!main || !(0,
|
|
2848
|
+
const main = typeof config.main === "string" ? (0, import_node_path8.resolve)(rootDir, config.main) : null;
|
|
2849
|
+
if (!main || !(0, import_node_fs10.existsSync)(main)) {
|
|
2607
2850
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
2608
2851
|
} else {
|
|
2609
2852
|
let source = "";
|
|
2610
2853
|
try {
|
|
2611
|
-
source = (0,
|
|
2854
|
+
source = (0, import_node_fs10.readFileSync)(main, "utf8");
|
|
2612
2855
|
} catch {
|
|
2613
2856
|
}
|
|
2614
2857
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -2632,7 +2875,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
2632
2875
|
}
|
|
2633
2876
|
function readPackageJson(rootDir) {
|
|
2634
2877
|
try {
|
|
2635
|
-
return JSON.parse((0,
|
|
2878
|
+
return JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path8.join)(rootDir, "package.json"), "utf8"));
|
|
2636
2879
|
} catch {
|
|
2637
2880
|
return null;
|
|
2638
2881
|
}
|
|
@@ -2859,13 +3102,14 @@ function harnessOption(value, flag) {
|
|
|
2859
3102
|
}
|
|
2860
3103
|
|
|
2861
3104
|
// src/init.ts
|
|
2862
|
-
var
|
|
2863
|
-
var
|
|
3105
|
+
var import_node_fs11 = require("fs");
|
|
3106
|
+
var import_node_path9 = require("path");
|
|
3107
|
+
var import_apps3 = require("@odla-ai/apps");
|
|
2864
3108
|
function initProject(options) {
|
|
2865
3109
|
const out = options.stdout ?? console;
|
|
2866
|
-
const rootDir = (0,
|
|
2867
|
-
const configPath = (0,
|
|
2868
|
-
if ((0,
|
|
3110
|
+
const rootDir = (0, import_node_path9.resolve)(options.rootDir ?? process.cwd());
|
|
3111
|
+
const configPath = (0, import_node_path9.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
3112
|
+
if ((0, import_node_fs11.existsSync)(configPath) && !options.force) {
|
|
2869
3113
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
2870
3114
|
}
|
|
2871
3115
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -2873,22 +3117,28 @@ function initProject(options) {
|
|
|
2873
3117
|
}
|
|
2874
3118
|
const envs = options.envs?.length ? options.envs : ["dev"];
|
|
2875
3119
|
const services = options.services?.length ? options.services : ["db", "ai"];
|
|
2876
|
-
|
|
3120
|
+
for (const service of services) {
|
|
3121
|
+
const definition = (0, import_apps3.appServiceDefinition)(service);
|
|
3122
|
+
if (!definition) throw new Error(`--services contains unknown service "${service}" (known: ${(0, import_apps3.appServiceIds)().join(", ")})`);
|
|
3123
|
+
for (const dependency of definition.requires) {
|
|
3124
|
+
if (!services.includes(dependency)) throw new Error(`--services ${service} requires ${dependency}`);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
2877
3127
|
const aiProvider = options.aiProvider ?? "anthropic";
|
|
2878
|
-
(0,
|
|
2879
|
-
(0,
|
|
2880
|
-
(0,
|
|
2881
|
-
(0,
|
|
2882
|
-
writeIfMissing((0,
|
|
2883
|
-
writeIfMissing((0,
|
|
3128
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.dirname)(configPath), { recursive: true });
|
|
3129
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
3130
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, ".odla"), { recursive: true });
|
|
3131
|
+
(0, import_node_fs11.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
3132
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
3133
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
2884
3134
|
ensureGitignore(rootDir);
|
|
2885
3135
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
2886
3136
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
2887
3137
|
out.log("updated .gitignore for local odla credentials");
|
|
2888
3138
|
}
|
|
2889
3139
|
function writeIfMissing(path, text) {
|
|
2890
|
-
if ((0,
|
|
2891
|
-
(0,
|
|
3140
|
+
if ((0, import_node_fs11.existsSync)(path)) return;
|
|
3141
|
+
(0, import_node_fs11.writeFileSync)(path, text);
|
|
2892
3142
|
}
|
|
2893
3143
|
function configTemplate(input) {
|
|
2894
3144
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -3063,7 +3313,7 @@ function assertWranglerConfig(cfg) {
|
|
|
3063
3313
|
|
|
3064
3314
|
// src/secrets-set.ts
|
|
3065
3315
|
var import_ai = require("@odla-ai/ai");
|
|
3066
|
-
var
|
|
3316
|
+
var import_apps4 = require("@odla-ai/apps");
|
|
3067
3317
|
var PROD_ENV_NAMES2 = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
3068
3318
|
async function secretsSet(options) {
|
|
3069
3319
|
const name = (options.name ?? "").trim();
|
|
@@ -3115,13 +3365,13 @@ async function resolveVaultWrite(options) {
|
|
|
3115
3365
|
throw new Error(`refusing to store a secret for "${options.env}" without --yes`);
|
|
3116
3366
|
}
|
|
3117
3367
|
const value = await secretInputValue(options, "secret");
|
|
3118
|
-
return { cfg, tenantId: (0,
|
|
3368
|
+
return { cfg, tenantId: (0, import_apps4.tenantIdFor)(cfg.app.id, options.env), value, doFetch, out };
|
|
3119
3369
|
}
|
|
3120
3370
|
|
|
3121
3371
|
// src/skill.ts
|
|
3122
|
-
var
|
|
3123
|
-
var
|
|
3124
|
-
var
|
|
3372
|
+
var import_node_fs12 = require("fs");
|
|
3373
|
+
var import_node_os2 = require("os");
|
|
3374
|
+
var import_node_path10 = require("path");
|
|
3125
3375
|
var import_node_url2 = require("url");
|
|
3126
3376
|
|
|
3127
3377
|
// src/skill-adapters.ts
|
|
@@ -3200,8 +3450,8 @@ function installSkill(options = {}) {
|
|
|
3200
3450
|
const files = listFiles(sourceDir);
|
|
3201
3451
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
3202
3452
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
3203
|
-
const root = (0,
|
|
3204
|
-
const home = (0,
|
|
3453
|
+
const root = (0, import_node_path10.resolve)(options.dir ?? process.cwd());
|
|
3454
|
+
const home = (0, import_node_path10.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
|
|
3205
3455
|
const plans = /* @__PURE__ */ new Map();
|
|
3206
3456
|
const targets = /* @__PURE__ */ new Map();
|
|
3207
3457
|
const rememberTarget = (harness, target) => {
|
|
@@ -3215,48 +3465,48 @@ function installSkill(options = {}) {
|
|
|
3215
3465
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
3216
3466
|
};
|
|
3217
3467
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
3218
|
-
for (const rel of files) plan((0,
|
|
3468
|
+
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);
|
|
3219
3469
|
};
|
|
3220
3470
|
let targetDir;
|
|
3221
3471
|
if (options.global) {
|
|
3222
|
-
const claudeRoot = (0,
|
|
3223
|
-
const codexRoot = (0,
|
|
3472
|
+
const claudeRoot = (0, import_node_path10.join)(home, ".claude", "skills");
|
|
3473
|
+
const codexRoot = (0, import_node_path10.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path10.join)(home, ".codex"), "skills");
|
|
3224
3474
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
3225
3475
|
for (const harness of harnesses) {
|
|
3226
3476
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
3227
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
3477
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path10.dirname)((0, import_node_path10.dirname)(codexRoot)));
|
|
3228
3478
|
rememberTarget(harness, skillRoot);
|
|
3229
3479
|
}
|
|
3230
3480
|
} else {
|
|
3231
|
-
const sharedRoot = (0,
|
|
3481
|
+
const sharedRoot = (0, import_node_path10.join)(root, ".agents", "skills");
|
|
3232
3482
|
planSkillTree(sharedRoot);
|
|
3233
|
-
const claudeRoot = (0,
|
|
3483
|
+
const claudeRoot = (0, import_node_path10.join)(root, ".claude", "skills");
|
|
3234
3484
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
3235
3485
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
3236
3486
|
if (harnesses.includes("claude")) {
|
|
3237
3487
|
for (const skill of skillNames(files)) {
|
|
3238
|
-
const canonical = (0,
|
|
3239
|
-
plan((0,
|
|
3488
|
+
const canonical = (0, import_node_fs12.readFileSync)((0, import_node_path10.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
3489
|
+
plan((0, import_node_path10.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
|
|
3240
3490
|
}
|
|
3241
3491
|
rememberTarget("claude", claudeRoot);
|
|
3242
3492
|
}
|
|
3243
3493
|
if (harnesses.includes("cursor")) {
|
|
3244
|
-
const cursorRule = (0,
|
|
3494
|
+
const cursorRule = (0, import_node_path10.join)(root, ".cursor", "rules", "odla.mdc");
|
|
3245
3495
|
plan(cursorRule, CURSOR_RULE);
|
|
3246
3496
|
rememberTarget("cursor", cursorRule);
|
|
3247
3497
|
}
|
|
3248
3498
|
if (harnesses.includes("agents")) {
|
|
3249
|
-
const agentsFile = (0,
|
|
3499
|
+
const agentsFile = (0, import_node_path10.join)(root, "AGENTS.md");
|
|
3250
3500
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3251
3501
|
rememberTarget("agents", agentsFile);
|
|
3252
3502
|
}
|
|
3253
3503
|
if (harnesses.includes("copilot")) {
|
|
3254
|
-
const copilotFile = (0,
|
|
3504
|
+
const copilotFile = (0, import_node_path10.join)(root, ".github", "copilot-instructions.md");
|
|
3255
3505
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3256
3506
|
rememberTarget("copilot", copilotFile);
|
|
3257
3507
|
}
|
|
3258
3508
|
if (harnesses.includes("gemini")) {
|
|
3259
|
-
const geminiFile = (0,
|
|
3509
|
+
const geminiFile = (0, import_node_path10.join)(root, "GEMINI.md");
|
|
3260
3510
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3261
3511
|
rememberTarget("gemini", geminiFile);
|
|
3262
3512
|
}
|
|
@@ -3270,11 +3520,11 @@ function installSkill(options = {}) {
|
|
|
3270
3520
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
3271
3521
|
continue;
|
|
3272
3522
|
}
|
|
3273
|
-
if (!(0,
|
|
3523
|
+
if (!(0, import_node_fs12.existsSync)(file.target)) {
|
|
3274
3524
|
writtenPaths.add(file.target);
|
|
3275
3525
|
continue;
|
|
3276
3526
|
}
|
|
3277
|
-
const current = (0,
|
|
3527
|
+
const current = (0, import_node_fs12.readFileSync)(file.target, "utf8");
|
|
3278
3528
|
if (current === file.content) {
|
|
3279
3529
|
unchangedPaths.add(file.target);
|
|
3280
3530
|
} else if (file.managedMerge || options.force) {
|
|
@@ -3291,9 +3541,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3291
3541
|
);
|
|
3292
3542
|
}
|
|
3293
3543
|
for (const file of plans.values()) {
|
|
3294
|
-
if (!(0,
|
|
3295
|
-
(0,
|
|
3296
|
-
(0,
|
|
3544
|
+
if (!(0, import_node_fs12.existsSync)(file.target) || (0, import_node_fs12.readFileSync)(file.target, "utf8") !== file.content) {
|
|
3545
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(file.target), { recursive: true });
|
|
3546
|
+
(0, import_node_fs12.writeFileSync)(file.target, file.content);
|
|
3297
3547
|
}
|
|
3298
3548
|
}
|
|
3299
3549
|
const skills = skillNames(files);
|
|
@@ -3312,7 +3562,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3312
3562
|
};
|
|
3313
3563
|
}
|
|
3314
3564
|
function pathsUnder(root, paths) {
|
|
3315
|
-
return [...paths].map((path) => (0,
|
|
3565
|
+
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();
|
|
3316
3566
|
}
|
|
3317
3567
|
function normalizeHarnesses(values, global) {
|
|
3318
3568
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -3334,9 +3584,9 @@ function normalizeHarnesses(values, global) {
|
|
|
3334
3584
|
function managedFileContent(path, block, force, boundary) {
|
|
3335
3585
|
const symlink = symlinkedComponent(boundary, path);
|
|
3336
3586
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
3337
|
-
if (!(0,
|
|
3587
|
+
if (!(0, import_node_fs12.existsSync)(path)) return `${block}
|
|
3338
3588
|
`;
|
|
3339
|
-
const current = (0,
|
|
3589
|
+
const current = (0, import_node_fs12.readFileSync)(path, "utf8");
|
|
3340
3590
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
3341
3591
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
3342
3592
|
const startAt = current.indexOf(start);
|
|
@@ -3357,15 +3607,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
3357
3607
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
3358
3608
|
}
|
|
3359
3609
|
function symlinkedComponent(boundary, target) {
|
|
3360
|
-
const rel = (0,
|
|
3361
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
3610
|
+
const rel = (0, import_node_path10.relative)(boundary, target);
|
|
3611
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(rel)) {
|
|
3362
3612
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
3363
3613
|
}
|
|
3364
3614
|
let current = boundary;
|
|
3365
|
-
for (const part of rel.split(
|
|
3366
|
-
current = (0,
|
|
3615
|
+
for (const part of rel.split(import_node_path10.sep).filter(Boolean)) {
|
|
3616
|
+
current = (0, import_node_path10.join)(current, part);
|
|
3367
3617
|
try {
|
|
3368
|
-
if ((0,
|
|
3618
|
+
if ((0, import_node_fs12.lstatSync)(current).isSymbolicLink()) return current;
|
|
3369
3619
|
} catch (error) {
|
|
3370
3620
|
if (error.code !== "ENOENT") throw error;
|
|
3371
3621
|
}
|
|
@@ -3376,13 +3626,13 @@ function skillNames(files) {
|
|
|
3376
3626
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
3377
3627
|
}
|
|
3378
3628
|
function listFiles(dir) {
|
|
3379
|
-
if (!(0,
|
|
3629
|
+
if (!(0, import_node_fs12.existsSync)(dir)) return [];
|
|
3380
3630
|
const results = [];
|
|
3381
3631
|
const walk = (current) => {
|
|
3382
|
-
for (const entry of (0,
|
|
3383
|
-
const path = (0,
|
|
3632
|
+
for (const entry of (0, import_node_fs12.readdirSync)(current, { withFileTypes: true })) {
|
|
3633
|
+
const path = (0, import_node_path10.join)(current, entry.name);
|
|
3384
3634
|
if (entry.isDirectory()) walk(path);
|
|
3385
|
-
else results.push((0,
|
|
3635
|
+
else results.push((0, import_node_path10.relative)(dir, path));
|
|
3386
3636
|
}
|
|
3387
3637
|
};
|
|
3388
3638
|
walk(dir);
|
|
@@ -3529,13 +3779,16 @@ async function safeText3(res) {
|
|
|
3529
3779
|
|
|
3530
3780
|
// src/cli-project.ts
|
|
3531
3781
|
var SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
3532
|
-
function install(parsed, positionals) {
|
|
3782
|
+
function install(parsed, positionals, deps) {
|
|
3533
3783
|
assertArgs(parsed, SKILL_OPTS, positionals);
|
|
3534
3784
|
installSkill({
|
|
3535
3785
|
dir: stringOpt(parsed.options.dir),
|
|
3536
3786
|
global: parsed.options.global === true,
|
|
3537
3787
|
harnesses: harnessList(parsed),
|
|
3538
|
-
force: parsed.options.force === true
|
|
3788
|
+
force: parsed.options.force === true,
|
|
3789
|
+
homeDir: deps.homeDir,
|
|
3790
|
+
codexHomeDir: deps.codexHomeDir,
|
|
3791
|
+
stdout: deps.stdout
|
|
3539
3792
|
});
|
|
3540
3793
|
}
|
|
3541
3794
|
async function secretsCommand(parsed, deps) {
|
|
@@ -3551,6 +3804,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
3551
3804
|
token: stringOpt(parsed.options.token),
|
|
3552
3805
|
email: stringOpt(parsed.options.email),
|
|
3553
3806
|
yes: parsed.options.yes === true,
|
|
3807
|
+
readStdin: deps.readStdin,
|
|
3554
3808
|
fetch: deps.fetch,
|
|
3555
3809
|
stdout: deps.stdout
|
|
3556
3810
|
};
|
|
@@ -3609,8 +3863,8 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3609
3863
|
return true;
|
|
3610
3864
|
}
|
|
3611
3865
|
if (command === "setup") {
|
|
3612
|
-
install(parsed, 1);
|
|
3613
|
-
console.log(
|
|
3866
|
+
install(parsed, 1, deps);
|
|
3867
|
+
(deps.stdout ?? console).log(
|
|
3614
3868
|
"\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."
|
|
3615
3869
|
);
|
|
3616
3870
|
return true;
|
|
@@ -3618,7 +3872,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3618
3872
|
if (command === "skill") {
|
|
3619
3873
|
const sub = parsed.positionals[1];
|
|
3620
3874
|
if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
|
|
3621
|
-
install(parsed, 2);
|
|
3875
|
+
install(parsed, 2, deps);
|
|
3622
3876
|
return true;
|
|
3623
3877
|
}
|
|
3624
3878
|
if (command === "secrets") {
|
|
@@ -3629,9 +3883,9 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3629
3883
|
}
|
|
3630
3884
|
|
|
3631
3885
|
// src/code-connect.ts
|
|
3632
|
-
var
|
|
3633
|
-
var
|
|
3634
|
-
var
|
|
3886
|
+
var import_node_fs14 = require("fs");
|
|
3887
|
+
var import_node_os4 = require("os");
|
|
3888
|
+
var import_node_path12 = require("path");
|
|
3635
3889
|
|
|
3636
3890
|
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
3637
3891
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -6516,9 +6770,9 @@ var CodePiRuntimeEngine = class {
|
|
|
6516
6770
|
};
|
|
6517
6771
|
|
|
6518
6772
|
// src/help.ts
|
|
6519
|
-
var
|
|
6773
|
+
var import_node_fs13 = require("fs");
|
|
6520
6774
|
function cliVersion() {
|
|
6521
|
-
const pkg = JSON.parse((0,
|
|
6775
|
+
const pkg = JSON.parse((0, import_node_fs13.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
|
|
6522
6776
|
return pkg.version ?? "unknown";
|
|
6523
6777
|
}
|
|
6524
6778
|
function printHelp(output = console) {
|
|
@@ -6606,15 +6860,16 @@ Usage:
|
|
|
6606
6860
|
odla-ai runbook rm <slug> [--app <id>]
|
|
6607
6861
|
odla-ai capabilities [--json]
|
|
6608
6862
|
odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
|
|
6609
|
-
odla-ai admin ai show [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6610
|
-
odla-ai admin ai models [--provider <id>] [--json]
|
|
6611
|
-
odla-ai admin ai set <purpose> [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
odla-ai admin ai
|
|
6616
|
-
odla-ai admin ai
|
|
6617
|
-
odla-ai admin ai
|
|
6863
|
+
odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6864
|
+
odla-ai admin ai models [--context <name>] [--provider <id>] [--json]
|
|
6865
|
+
odla-ai admin ai set <purpose> [--context <name>] [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6866
|
+
[--max-input-bytes <n>] [--max-output-tokens <n>] [--max-calls-per-run <n>] [--json]
|
|
6867
|
+
odla-ai admin ai set security [--context <name>] --discovery-provider <id> --discovery-model <id>
|
|
6868
|
+
--validation-provider <id> --validation-model <id> [--enabled|--no-enabled] [--json]
|
|
6869
|
+
odla-ai admin ai credentials [--context <name>] [--json]
|
|
6870
|
+
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
6871
|
+
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
6872
|
+
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
6618
6873
|
odla-ai security github connect [--repo owner/name] [--env dev] [--email <odla-account>] [--no-open]
|
|
6619
6874
|
odla-ai security github disconnect --source <id> [--env dev] [--yes]
|
|
6620
6875
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -7041,8 +7296,8 @@ function digestText(value) {
|
|
|
7041
7296
|
var import_node_child_process6 = require("child_process");
|
|
7042
7297
|
var import_node_crypto2 = require("crypto");
|
|
7043
7298
|
var import_promises10 = require("fs/promises");
|
|
7044
|
-
var
|
|
7045
|
-
var
|
|
7299
|
+
var import_node_os3 = require("os");
|
|
7300
|
+
var import_node_path11 = require("path");
|
|
7046
7301
|
var import_node_url3 = require("url");
|
|
7047
7302
|
|
|
7048
7303
|
// src/code-runtime-config.ts
|
|
@@ -7128,10 +7383,10 @@ async function embeddedPiImageName() {
|
|
|
7128
7383
|
return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto2.createHash)("sha256").update(bundle).digest("hex")}`;
|
|
7129
7384
|
}
|
|
7130
7385
|
async function buildEmbeddedPiImage(engine, image, run) {
|
|
7131
|
-
const context = await (0, import_promises10.mkdtemp)((0,
|
|
7386
|
+
const context = await (0, import_promises10.mkdtemp)((0, import_node_path11.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
|
|
7132
7387
|
try {
|
|
7133
|
-
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0,
|
|
7134
|
-
await (0, import_promises10.writeFile)((0,
|
|
7388
|
+
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path11.join)(context, "pi-agent.js"));
|
|
7389
|
+
await (0, import_promises10.writeFile)((0, import_node_path11.join)(context, "Dockerfile"), [
|
|
7135
7390
|
`FROM ${CODE_NODE_IMAGE}`,
|
|
7136
7391
|
"COPY pi-agent.js /opt/odla/pi-agent.js",
|
|
7137
7392
|
"WORKDIR /workspace",
|
|
@@ -7147,8 +7402,8 @@ async function buildEmbeddedPiImage(engine, image, run) {
|
|
|
7147
7402
|
// src/code-connect.ts
|
|
7148
7403
|
async function codeConnect(options) {
|
|
7149
7404
|
const cwd = options.cwd ?? process.cwd();
|
|
7150
|
-
const configPath = (0,
|
|
7151
|
-
const cfg = (0,
|
|
7405
|
+
const configPath = (0, import_node_path12.resolve)(cwd, options.configPath);
|
|
7406
|
+
const cfg = (0, import_node_fs14.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
7152
7407
|
const requestedAppId = options.appId?.trim();
|
|
7153
7408
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
7154
7409
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -7182,7 +7437,7 @@ async function codeConnect(options) {
|
|
|
7182
7437
|
);
|
|
7183
7438
|
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");
|
|
7184
7439
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
7185
|
-
const hostName = (options.name ?? (0,
|
|
7440
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
7186
7441
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
7187
7442
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
7188
7443
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -7219,8 +7474,8 @@ async function codeConnect(options) {
|
|
|
7219
7474
|
platform: hostPlatform,
|
|
7220
7475
|
arch: process.arch,
|
|
7221
7476
|
engines: [engine],
|
|
7222
|
-
cpuCount: (0,
|
|
7223
|
-
memoryBytes: (0,
|
|
7477
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
7478
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
7224
7479
|
source: descriptor2,
|
|
7225
7480
|
images: {
|
|
7226
7481
|
ready: true,
|
|
@@ -7366,246 +7621,19 @@ async function codeCommand(parsed, dependencies) {
|
|
|
7366
7621
|
}
|
|
7367
7622
|
|
|
7368
7623
|
// src/operator-credentials.ts
|
|
7369
|
-
var
|
|
7624
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
7370
7625
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
7371
7626
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
7372
7627
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
7373
|
-
const source =
|
|
7628
|
+
const source = clean3(
|
|
7374
7629
|
stringOpt(parsed.options.token)
|
|
7375
|
-
) ? "flag" :
|
|
7630
|
+
) ? "flag" : clean3(import_node_process9.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
7376
7631
|
return {
|
|
7377
7632
|
source,
|
|
7378
7633
|
cacheFile: context.cfg.local.tokenFile,
|
|
7379
7634
|
cacheStatus
|
|
7380
7635
|
};
|
|
7381
7636
|
}
|
|
7382
|
-
function clean(value) {
|
|
7383
|
-
const normalized = value?.trim();
|
|
7384
|
-
return normalized || void 0;
|
|
7385
|
-
}
|
|
7386
|
-
|
|
7387
|
-
// src/operator-context.ts
|
|
7388
|
-
var import_node_fs14 = require("fs");
|
|
7389
|
-
var import_node_path12 = require("path");
|
|
7390
|
-
var import_node_process9 = __toESM(require("process"), 1);
|
|
7391
|
-
|
|
7392
|
-
// src/operator-profiles.ts
|
|
7393
|
-
var import_node_fs13 = require("fs");
|
|
7394
|
-
var import_node_os4 = require("os");
|
|
7395
|
-
var import_node_path11 = require("path");
|
|
7396
|
-
var import_node_process8 = __toESM(require("process"), 1);
|
|
7397
|
-
function operatorProfileFile() {
|
|
7398
|
-
return (0, import_node_path11.resolve)(
|
|
7399
|
-
clean2(import_node_process8.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".odla", "contexts.json")
|
|
7400
|
-
);
|
|
7401
|
-
}
|
|
7402
|
-
function resolveOperatorProfile(parsed) {
|
|
7403
|
-
const fromFlag = clean2(stringOpt(parsed.options.context));
|
|
7404
|
-
const fromEnvironment = clean2(import_node_process8.default.env.ODLA_CONTEXT);
|
|
7405
|
-
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7406
|
-
const file = operatorProfileFile();
|
|
7407
|
-
if (!name) {
|
|
7408
|
-
return { name: null, source: "unresolved", file, value: null };
|
|
7409
|
-
}
|
|
7410
|
-
assertOperatorName(name, "context");
|
|
7411
|
-
const profiles = readOperatorProfiles(file);
|
|
7412
|
-
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
7413
|
-
if (!value) {
|
|
7414
|
-
throw new Error(
|
|
7415
|
-
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
7416
|
-
);
|
|
7417
|
-
}
|
|
7418
|
-
return {
|
|
7419
|
-
name,
|
|
7420
|
-
source: fromFlag ? "flag" : "environment",
|
|
7421
|
-
file,
|
|
7422
|
-
value
|
|
7423
|
-
};
|
|
7424
|
-
}
|
|
7425
|
-
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
7426
|
-
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
7427
|
-
}
|
|
7428
|
-
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
7429
|
-
assertOperatorName(name, "context");
|
|
7430
|
-
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
7431
|
-
const profiles = readOperatorProfiles(file);
|
|
7432
|
-
profiles[name] = normalized;
|
|
7433
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7434
|
-
}
|
|
7435
|
-
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
7436
|
-
assertOperatorName(name, "context");
|
|
7437
|
-
const profiles = readOperatorProfiles(file);
|
|
7438
|
-
if (!Object.hasOwn(profiles, name)) return false;
|
|
7439
|
-
delete profiles[name];
|
|
7440
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7441
|
-
return true;
|
|
7442
|
-
}
|
|
7443
|
-
function operatorCredentialFiles(selection) {
|
|
7444
|
-
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");
|
|
7445
|
-
return {
|
|
7446
|
-
developer: (0, import_node_path11.join)(base, "dev-token.json"),
|
|
7447
|
-
scoped: (0, import_node_path11.join)(base, "admin-token.local.json")
|
|
7448
|
-
};
|
|
7449
|
-
}
|
|
7450
|
-
function assertOperatorName(value, label) {
|
|
7451
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
7452
|
-
throw new Error(
|
|
7453
|
-
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
7454
|
-
);
|
|
7455
|
-
}
|
|
7456
|
-
}
|
|
7457
|
-
function readOperatorProfiles(file) {
|
|
7458
|
-
if (!(0, import_node_fs13.existsSync)(file)) return emptyProfiles();
|
|
7459
|
-
let raw;
|
|
7460
|
-
try {
|
|
7461
|
-
raw = JSON.parse((0, import_node_fs13.readFileSync)(file, "utf8"));
|
|
7462
|
-
} catch {
|
|
7463
|
-
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
7464
|
-
}
|
|
7465
|
-
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
7466
|
-
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
7467
|
-
}
|
|
7468
|
-
const unknown = Object.keys(raw).filter(
|
|
7469
|
-
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
7470
|
-
);
|
|
7471
|
-
if (unknown.length > 0) {
|
|
7472
|
-
throw new Error(
|
|
7473
|
-
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
7474
|
-
);
|
|
7475
|
-
}
|
|
7476
|
-
const profiles = emptyProfiles();
|
|
7477
|
-
for (const [name, value] of Object.entries(
|
|
7478
|
-
raw.profiles
|
|
7479
|
-
)) {
|
|
7480
|
-
assertOperatorName(name, "context");
|
|
7481
|
-
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
7482
|
-
}
|
|
7483
|
-
return profiles;
|
|
7484
|
-
}
|
|
7485
|
-
function emptyProfiles() {
|
|
7486
|
-
return /* @__PURE__ */ Object.create(null);
|
|
7487
|
-
}
|
|
7488
|
-
function validateProfile(value, label) {
|
|
7489
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7490
|
-
throw new Error(`${label} has an invalid shape`);
|
|
7491
|
-
}
|
|
7492
|
-
const unknown = Object.keys(value).filter(
|
|
7493
|
-
(key) => !["platform", "app", "environment"].includes(key)
|
|
7494
|
-
);
|
|
7495
|
-
if (unknown.length > 0) {
|
|
7496
|
-
throw new Error(
|
|
7497
|
-
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
7498
|
-
);
|
|
7499
|
-
}
|
|
7500
|
-
const candidate = value;
|
|
7501
|
-
if (typeof candidate.platform !== "string") {
|
|
7502
|
-
throw new Error(`${label} needs an absolute platform URL`);
|
|
7503
|
-
}
|
|
7504
|
-
const platform = platformAudience(candidate.platform);
|
|
7505
|
-
const app = clean2(candidate.app);
|
|
7506
|
-
const environment2 = clean2(candidate.environment);
|
|
7507
|
-
if (app) assertOperatorName(app, "app");
|
|
7508
|
-
if (environment2) assertOperatorName(environment2, "environment");
|
|
7509
|
-
return {
|
|
7510
|
-
platform,
|
|
7511
|
-
...app ? { app } : {},
|
|
7512
|
-
...environment2 ? { environment: environment2 } : {}
|
|
7513
|
-
};
|
|
7514
|
-
}
|
|
7515
|
-
function clean2(value) {
|
|
7516
|
-
const normalized = value?.trim();
|
|
7517
|
-
return normalized || void 0;
|
|
7518
|
-
}
|
|
7519
|
-
|
|
7520
|
-
// src/operator-context.ts
|
|
7521
|
-
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
7522
|
-
async function resolveOperatorContext(parsed, options = {}) {
|
|
7523
|
-
const profile = resolveOperatorProfile(parsed);
|
|
7524
|
-
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
7525
|
-
const configPath = (0, import_node_path12.resolve)(configArgument);
|
|
7526
|
-
const explicitConfig = parsed.options.config !== void 0;
|
|
7527
|
-
const hasConfig = (0, import_node_fs14.existsSync)(configPath);
|
|
7528
|
-
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
7529
|
-
await loadProjectConfig(configArgument);
|
|
7530
|
-
}
|
|
7531
|
-
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
7532
|
-
const platformFlag = clean3(stringOpt(parsed.options.platform));
|
|
7533
|
-
const platformEnvironment = clean3(import_node_process9.default.env.ODLA_PLATFORM_URL);
|
|
7534
|
-
const platformValue = platformAudience(
|
|
7535
|
-
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
7536
|
-
);
|
|
7537
|
-
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
7538
|
-
const appFlag = clean3(stringOpt(parsed.options.app));
|
|
7539
|
-
const appEnvironment = clean3(import_node_process9.default.env.ODLA_APP_ID);
|
|
7540
|
-
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
7541
|
-
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
7542
|
-
if (appValue) assertOperatorName(appValue, "app");
|
|
7543
|
-
if (options.requireApp && !appValue) {
|
|
7544
|
-
throw new Error(
|
|
7545
|
-
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
7546
|
-
);
|
|
7547
|
-
}
|
|
7548
|
-
const envFlag = clean3(stringOpt(parsed.options.env));
|
|
7549
|
-
const envEnvironment = clean3(import_node_process9.default.env.ODLA_ENV);
|
|
7550
|
-
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
7551
|
-
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
7552
|
-
if (environmentValue) {
|
|
7553
|
-
assertOperatorName(environmentValue, "environment");
|
|
7554
|
-
}
|
|
7555
|
-
const rootDir = loaded?.rootDir ?? import_node_process9.default.cwd();
|
|
7556
|
-
const profileCredentials = operatorCredentialFiles(profile);
|
|
7557
|
-
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;
|
|
7558
|
-
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;
|
|
7559
|
-
const cfg = loaded ? {
|
|
7560
|
-
...loaded,
|
|
7561
|
-
platformUrl: platformValue,
|
|
7562
|
-
app: appValue ? {
|
|
7563
|
-
id: appValue,
|
|
7564
|
-
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
7565
|
-
} : loaded.app,
|
|
7566
|
-
local: { ...loaded.local, tokenFile }
|
|
7567
|
-
} : {
|
|
7568
|
-
configPath,
|
|
7569
|
-
rootDir,
|
|
7570
|
-
platformUrl: platformValue,
|
|
7571
|
-
dbEndpoint: platformValue,
|
|
7572
|
-
app: {
|
|
7573
|
-
id: appValue ?? "operator",
|
|
7574
|
-
name: appValue ?? "Operator"
|
|
7575
|
-
},
|
|
7576
|
-
envs: environmentValue ? [environmentValue] : [],
|
|
7577
|
-
services: [],
|
|
7578
|
-
local: {
|
|
7579
|
-
tokenFile,
|
|
7580
|
-
credentialsFile: (0, import_node_path12.join)(rootDir, ".odla", "credentials.local.json"),
|
|
7581
|
-
devVarsFile: (0, import_node_path12.join)(rootDir, ".dev.vars"),
|
|
7582
|
-
gitignore: true
|
|
7583
|
-
}
|
|
7584
|
-
};
|
|
7585
|
-
return {
|
|
7586
|
-
cfg,
|
|
7587
|
-
profile: {
|
|
7588
|
-
name: profile.name,
|
|
7589
|
-
source: profile.source,
|
|
7590
|
-
file: profile.file
|
|
7591
|
-
},
|
|
7592
|
-
config: {
|
|
7593
|
-
path: configPath,
|
|
7594
|
-
status: loaded ? "loaded" : "absent",
|
|
7595
|
-
explicit: explicitConfig
|
|
7596
|
-
},
|
|
7597
|
-
platform: { value: platformValue, source: platformSource },
|
|
7598
|
-
app: { value: appValue, source: appSource },
|
|
7599
|
-
environment: {
|
|
7600
|
-
value: environmentValue,
|
|
7601
|
-
source: environmentSource
|
|
7602
|
-
},
|
|
7603
|
-
credentials: {
|
|
7604
|
-
developerTokenFile: tokenFile,
|
|
7605
|
-
scopedTokenFile
|
|
7606
|
-
}
|
|
7607
|
-
};
|
|
7608
|
-
}
|
|
7609
7637
|
function clean3(value) {
|
|
7610
7638
|
const normalized = value?.trim();
|
|
7611
7639
|
return normalized || void 0;
|
|
@@ -8965,7 +8993,7 @@ async function read2(url, headers, doFetch) {
|
|
|
8965
8993
|
}
|
|
8966
8994
|
|
|
8967
8995
|
// src/provision.ts
|
|
8968
|
-
var
|
|
8996
|
+
var import_apps7 = require("@odla-ai/apps");
|
|
8969
8997
|
var import_ai3 = require("@odla-ai/ai");
|
|
8970
8998
|
var import_node_process10 = __toESM(require("process"), 1);
|
|
8971
8999
|
|
|
@@ -9022,9 +9050,9 @@ async function responseText(res) {
|
|
|
9022
9050
|
}
|
|
9023
9051
|
|
|
9024
9052
|
// src/provision-credentials.ts
|
|
9025
|
-
var
|
|
9053
|
+
var import_apps5 = require("@odla-ai/apps");
|
|
9026
9054
|
async function provisionEnvCredentials(opts) {
|
|
9027
|
-
const tenantId = (0,
|
|
9055
|
+
const tenantId = (0, import_apps5.tenantIdFor)(opts.cfg.app.id, opts.env);
|
|
9028
9056
|
const prior = opts.credentials?.envs[opts.env];
|
|
9029
9057
|
let credentials = opts.credentials;
|
|
9030
9058
|
let dbKey = opts.cfg.services.includes("db") && !opts.rotateDb ? prior?.dbKey : void 0;
|
|
@@ -9118,18 +9146,13 @@ async function safeText4(res) {
|
|
|
9118
9146
|
|
|
9119
9147
|
// src/provision-helpers.ts
|
|
9120
9148
|
var import_ai2 = require("@odla-ai/ai");
|
|
9121
|
-
var
|
|
9122
|
-
function serviceRank(service) {
|
|
9123
|
-
if (service === "db") return 0;
|
|
9124
|
-
if (service === "calendar") return 2;
|
|
9125
|
-
return 1;
|
|
9126
|
-
}
|
|
9149
|
+
var import_apps6 = require("@odla-ai/apps");
|
|
9127
9150
|
function defaultSecretName(provider) {
|
|
9128
9151
|
const names = import_ai2.DEFAULT_SECRET_NAMES;
|
|
9129
9152
|
return names[provider] ?? `${provider}_api_key`;
|
|
9130
9153
|
}
|
|
9131
9154
|
async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
9132
|
-
const tenantId = (0,
|
|
9155
|
+
const tenantId = (0, import_apps6.tenantIdFor)(cfg.app.id, env);
|
|
9133
9156
|
const res = await doFetch(`${cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/entitlements`, {
|
|
9134
9157
|
headers: { authorization: `Bearer ${token}` }
|
|
9135
9158
|
});
|
|
@@ -9238,7 +9261,7 @@ async function provision(options) {
|
|
|
9238
9261
|
}
|
|
9239
9262
|
const doFetch = options.fetch ?? fetch;
|
|
9240
9263
|
const token = await getDeveloperToken(cfg, options, doFetch, out);
|
|
9241
|
-
const apps = (0,
|
|
9264
|
+
const apps = (0, import_apps7.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
|
|
9242
9265
|
const existing = await apps.resolveApp(cfg.app.id);
|
|
9243
9266
|
if (existing) {
|
|
9244
9267
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
@@ -9249,7 +9272,7 @@ async function provision(options) {
|
|
|
9249
9272
|
for (const env of cfg.envs) {
|
|
9250
9273
|
await assertTenantAdminAccess(doFetch, cfg, env, token);
|
|
9251
9274
|
}
|
|
9252
|
-
const serviceOrder =
|
|
9275
|
+
const serviceOrder = (0, import_apps7.orderAppServices)(cfg.services);
|
|
9253
9276
|
for (const env of cfg.envs) {
|
|
9254
9277
|
for (const service of serviceOrder) {
|
|
9255
9278
|
if (service === "ai") {
|
|
@@ -9282,7 +9305,7 @@ async function provision(options) {
|
|
|
9282
9305
|
}
|
|
9283
9306
|
}
|
|
9284
9307
|
for (const env of cfg.envs) {
|
|
9285
|
-
const tenantId = (0,
|
|
9308
|
+
const tenantId = (0, import_apps7.tenantIdFor)(cfg.app.id, env);
|
|
9286
9309
|
credentials = await provisionEnvCredentials({
|
|
9287
9310
|
cfg,
|
|
9288
9311
|
env,
|
|
@@ -11206,7 +11229,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
11206
11229
|
return;
|
|
11207
11230
|
}
|
|
11208
11231
|
if (command === "admin") {
|
|
11209
|
-
await adminCommand(parsed);
|
|
11232
|
+
await adminCommand(parsed, runtime);
|
|
11210
11233
|
return;
|
|
11211
11234
|
}
|
|
11212
11235
|
if (command === "agent") {
|