@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/bin.cjs
CHANGED
|
@@ -915,8 +915,8 @@ function parseArgv(argv) {
|
|
|
915
915
|
}
|
|
916
916
|
return { positionals, options };
|
|
917
917
|
}
|
|
918
|
-
function assertArgs(parsed,
|
|
919
|
-
const allowed = new Set(
|
|
918
|
+
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
919
|
+
const allowed = new Set(allowedOptions2);
|
|
920
920
|
for (const name of Object.keys(parsed.options)) {
|
|
921
921
|
if (!allowed.has(name)) throw new Error(`unknown option "--${name}"; run "odla-ai help" for supported options`);
|
|
922
922
|
}
|
|
@@ -959,66 +959,10 @@ 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");
|
|
@@ -1304,6 +1248,291 @@ function unique2(values) {
|
|
|
1304
1248
|
return [...new Set(values.filter(Boolean))];
|
|
1305
1249
|
}
|
|
1306
1250
|
|
|
1251
|
+
// src/operator-profiles.ts
|
|
1252
|
+
var import_node_fs5 = require("fs");
|
|
1253
|
+
var import_node_os = require("os");
|
|
1254
|
+
var import_node_path5 = require("path");
|
|
1255
|
+
var import_node_process7 = __toESM(require("process"), 1);
|
|
1256
|
+
function operatorProfileFile() {
|
|
1257
|
+
return (0, import_node_path5.resolve)(
|
|
1258
|
+
clean(import_node_process7.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path5.join)((0, import_node_os.homedir)(), ".odla", "contexts.json")
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
function resolveOperatorProfile(parsed) {
|
|
1262
|
+
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1263
|
+
const fromEnvironment = clean(import_node_process7.default.env.ODLA_CONTEXT);
|
|
1264
|
+
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1265
|
+
const file = operatorProfileFile();
|
|
1266
|
+
if (!name) {
|
|
1267
|
+
return { name: null, source: "unresolved", file, value: null };
|
|
1268
|
+
}
|
|
1269
|
+
assertOperatorName(name, "context");
|
|
1270
|
+
const profiles = readOperatorProfiles(file);
|
|
1271
|
+
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
1272
|
+
if (!value) {
|
|
1273
|
+
throw new Error(
|
|
1274
|
+
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
return {
|
|
1278
|
+
name,
|
|
1279
|
+
source: fromFlag ? "flag" : "environment",
|
|
1280
|
+
file,
|
|
1281
|
+
value
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
1285
|
+
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
1286
|
+
}
|
|
1287
|
+
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
1288
|
+
assertOperatorName(name, "context");
|
|
1289
|
+
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
1290
|
+
const profiles = readOperatorProfiles(file);
|
|
1291
|
+
profiles[name] = normalized;
|
|
1292
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1293
|
+
}
|
|
1294
|
+
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
1295
|
+
assertOperatorName(name, "context");
|
|
1296
|
+
const profiles = readOperatorProfiles(file);
|
|
1297
|
+
if (!Object.hasOwn(profiles, name)) return false;
|
|
1298
|
+
delete profiles[name];
|
|
1299
|
+
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
function operatorCredentialFiles(selection) {
|
|
1303
|
+
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");
|
|
1304
|
+
return {
|
|
1305
|
+
developer: (0, import_node_path5.join)(base, "dev-token.json"),
|
|
1306
|
+
scoped: (0, import_node_path5.join)(base, "admin-token.local.json")
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
function assertOperatorName(value, label) {
|
|
1310
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
1311
|
+
throw new Error(
|
|
1312
|
+
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
function readOperatorProfiles(file) {
|
|
1317
|
+
if (!(0, import_node_fs5.existsSync)(file)) return emptyProfiles();
|
|
1318
|
+
let raw;
|
|
1319
|
+
try {
|
|
1320
|
+
raw = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
|
|
1321
|
+
} catch {
|
|
1322
|
+
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1323
|
+
}
|
|
1324
|
+
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
1325
|
+
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
1326
|
+
}
|
|
1327
|
+
const unknown = Object.keys(raw).filter(
|
|
1328
|
+
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
1329
|
+
);
|
|
1330
|
+
if (unknown.length > 0) {
|
|
1331
|
+
throw new Error(
|
|
1332
|
+
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
const profiles = emptyProfiles();
|
|
1336
|
+
for (const [name, value] of Object.entries(
|
|
1337
|
+
raw.profiles
|
|
1338
|
+
)) {
|
|
1339
|
+
assertOperatorName(name, "context");
|
|
1340
|
+
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
1341
|
+
}
|
|
1342
|
+
return profiles;
|
|
1343
|
+
}
|
|
1344
|
+
function emptyProfiles() {
|
|
1345
|
+
return /* @__PURE__ */ Object.create(null);
|
|
1346
|
+
}
|
|
1347
|
+
function validateProfile(value, label) {
|
|
1348
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1349
|
+
throw new Error(`${label} has an invalid shape`);
|
|
1350
|
+
}
|
|
1351
|
+
const unknown = Object.keys(value).filter(
|
|
1352
|
+
(key) => !["platform", "app", "environment"].includes(key)
|
|
1353
|
+
);
|
|
1354
|
+
if (unknown.length > 0) {
|
|
1355
|
+
throw new Error(
|
|
1356
|
+
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
const candidate = value;
|
|
1360
|
+
if (typeof candidate.platform !== "string") {
|
|
1361
|
+
throw new Error(`${label} needs an absolute platform URL`);
|
|
1362
|
+
}
|
|
1363
|
+
const platform = platformAudience(candidate.platform);
|
|
1364
|
+
const app = clean(candidate.app);
|
|
1365
|
+
const environment2 = clean(candidate.environment);
|
|
1366
|
+
if (app) assertOperatorName(app, "app");
|
|
1367
|
+
if (environment2) assertOperatorName(environment2, "environment");
|
|
1368
|
+
return {
|
|
1369
|
+
platform,
|
|
1370
|
+
...app ? { app } : {},
|
|
1371
|
+
...environment2 ? { environment: environment2 } : {}
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
function clean(value) {
|
|
1375
|
+
const normalized = value?.trim();
|
|
1376
|
+
return normalized || void 0;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/operator-context.ts
|
|
1380
|
+
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
1381
|
+
async function resolveOperatorContext(parsed, options = {}) {
|
|
1382
|
+
const profile = resolveOperatorProfile(parsed);
|
|
1383
|
+
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1384
|
+
const configPath = (0, import_node_path6.resolve)(configArgument);
|
|
1385
|
+
const explicitConfig = parsed.options.config !== void 0;
|
|
1386
|
+
const hasConfig = (0, import_node_fs6.existsSync)(configPath);
|
|
1387
|
+
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1388
|
+
await loadProjectConfig(configArgument);
|
|
1389
|
+
}
|
|
1390
|
+
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1391
|
+
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1392
|
+
const platformEnvironment = clean2(import_node_process8.default.env.ODLA_PLATFORM_URL);
|
|
1393
|
+
const platformValue = platformAudience(
|
|
1394
|
+
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1395
|
+
);
|
|
1396
|
+
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1397
|
+
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1398
|
+
const appEnvironment = clean2(import_node_process8.default.env.ODLA_APP_ID);
|
|
1399
|
+
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1400
|
+
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1401
|
+
if (appValue) assertOperatorName(appValue, "app");
|
|
1402
|
+
if (options.requireApp && !appValue) {
|
|
1403
|
+
throw new Error(
|
|
1404
|
+
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1408
|
+
const envEnvironment = clean2(import_node_process8.default.env.ODLA_ENV);
|
|
1409
|
+
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1410
|
+
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1411
|
+
if (environmentValue) {
|
|
1412
|
+
assertOperatorName(environmentValue, "environment");
|
|
1413
|
+
}
|
|
1414
|
+
const rootDir = loaded?.rootDir ?? import_node_process8.default.cwd();
|
|
1415
|
+
const profileCredentials = operatorCredentialFiles(profile);
|
|
1416
|
+
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;
|
|
1417
|
+
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;
|
|
1418
|
+
const cfg = loaded ? {
|
|
1419
|
+
...loaded,
|
|
1420
|
+
platformUrl: platformValue,
|
|
1421
|
+
app: appValue ? {
|
|
1422
|
+
id: appValue,
|
|
1423
|
+
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
1424
|
+
} : loaded.app,
|
|
1425
|
+
local: { ...loaded.local, tokenFile }
|
|
1426
|
+
} : {
|
|
1427
|
+
configPath,
|
|
1428
|
+
rootDir,
|
|
1429
|
+
platformUrl: platformValue,
|
|
1430
|
+
dbEndpoint: platformValue,
|
|
1431
|
+
app: {
|
|
1432
|
+
id: appValue ?? "operator",
|
|
1433
|
+
name: appValue ?? "Operator"
|
|
1434
|
+
},
|
|
1435
|
+
envs: environmentValue ? [environmentValue] : [],
|
|
1436
|
+
services: [],
|
|
1437
|
+
local: {
|
|
1438
|
+
tokenFile,
|
|
1439
|
+
credentialsFile: (0, import_node_path6.join)(rootDir, ".odla", "credentials.local.json"),
|
|
1440
|
+
devVarsFile: (0, import_node_path6.join)(rootDir, ".dev.vars"),
|
|
1441
|
+
gitignore: true
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
return {
|
|
1445
|
+
cfg,
|
|
1446
|
+
profile: {
|
|
1447
|
+
name: profile.name,
|
|
1448
|
+
source: profile.source,
|
|
1449
|
+
file: profile.file
|
|
1450
|
+
},
|
|
1451
|
+
config: {
|
|
1452
|
+
path: configPath,
|
|
1453
|
+
status: loaded ? "loaded" : "absent",
|
|
1454
|
+
explicit: explicitConfig
|
|
1455
|
+
},
|
|
1456
|
+
platform: { value: platformValue, source: platformSource },
|
|
1457
|
+
app: { value: appValue, source: appSource },
|
|
1458
|
+
environment: {
|
|
1459
|
+
value: environmentValue,
|
|
1460
|
+
source: environmentSource
|
|
1461
|
+
},
|
|
1462
|
+
credentials: {
|
|
1463
|
+
developerTokenFile: tokenFile,
|
|
1464
|
+
scopedTokenFile
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function clean2(value) {
|
|
1469
|
+
const normalized = value?.trim();
|
|
1470
|
+
return normalized || void 0;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// src/admin-command.ts
|
|
1474
|
+
var CONTEXT_OPTIONS = ["platform", "config", "context", "token", "open", "email"];
|
|
1475
|
+
var JSON_OPTIONS = [...CONTEXT_OPTIONS, "json"];
|
|
1476
|
+
var SET_OPTIONS = [
|
|
1477
|
+
...JSON_OPTIONS,
|
|
1478
|
+
"provider",
|
|
1479
|
+
"model",
|
|
1480
|
+
"enabled",
|
|
1481
|
+
"max-input-bytes",
|
|
1482
|
+
"max-output-tokens",
|
|
1483
|
+
"max-calls-per-run",
|
|
1484
|
+
"discovery-provider",
|
|
1485
|
+
"discovery-model",
|
|
1486
|
+
"validation-provider",
|
|
1487
|
+
"validation-model"
|
|
1488
|
+
];
|
|
1489
|
+
async function adminCommand(parsed, deps = {}) {
|
|
1490
|
+
const area = parsed.positionals[1];
|
|
1491
|
+
const action = parsed.positionals[2];
|
|
1492
|
+
const credentialSet = action === "credential" && parsed.positionals[3] === "set";
|
|
1493
|
+
const credentials = action === "credentials";
|
|
1494
|
+
const models = action === "models";
|
|
1495
|
+
const usage = action === "usage";
|
|
1496
|
+
const audit = action === "audit";
|
|
1497
|
+
if (area !== "ai" || action !== "show" && action !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
1498
|
+
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
1499
|
+
}
|
|
1500
|
+
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;
|
|
1501
|
+
assertArgs(parsed, allowed, credentialSet ? 5 : action === "set" ? 4 : 3);
|
|
1502
|
+
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
1503
|
+
await adminAi({
|
|
1504
|
+
action: credentialSet ? "credential-set" : credentials ? "credentials" : models ? "models" : usage ? "usage" : audit ? "audit" : action,
|
|
1505
|
+
purpose: action === "set" ? parsed.positionals[3] : void 0,
|
|
1506
|
+
provider: stringOpt(parsed.options.provider),
|
|
1507
|
+
model: stringOpt(parsed.options.model),
|
|
1508
|
+
enabled: boolOpt(parsed.options.enabled),
|
|
1509
|
+
maxInputBytes: numberOpt(parsed.options["max-input-bytes"], "--max-input-bytes"),
|
|
1510
|
+
maxOutputTokens: numberOpt(parsed.options["max-output-tokens"], "--max-output-tokens"),
|
|
1511
|
+
maxCallsPerRun: numberOpt(parsed.options["max-calls-per-run"], "--max-calls-per-run"),
|
|
1512
|
+
platform: context.platform.value,
|
|
1513
|
+
token: stringOpt(parsed.options.token),
|
|
1514
|
+
tokenFile: context.credentials.scopedTokenFile,
|
|
1515
|
+
email: stringOpt(parsed.options.email),
|
|
1516
|
+
json: parsed.options.json === true,
|
|
1517
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1518
|
+
credentialProvider: credentialSet ? parsed.positionals[4] : void 0,
|
|
1519
|
+
fromEnv: stringOpt(parsed.options["from-env"]),
|
|
1520
|
+
stdin: parsed.options.stdin === true,
|
|
1521
|
+
readStdin: deps.readStdin,
|
|
1522
|
+
discoveryProvider: stringOpt(parsed.options["discovery-provider"]),
|
|
1523
|
+
discoveryModel: stringOpt(parsed.options["discovery-model"]),
|
|
1524
|
+
validationProvider: stringOpt(parsed.options["validation-provider"]),
|
|
1525
|
+
validationModel: stringOpt(parsed.options["validation-model"]),
|
|
1526
|
+
appId: stringOpt(parsed.options["app-id"]),
|
|
1527
|
+
env: stringOpt(parsed.options.env),
|
|
1528
|
+
runId: stringOpt(parsed.options["run-id"]),
|
|
1529
|
+
limit: numberOpt(parsed.options.limit, "--limit"),
|
|
1530
|
+
fetch: deps.fetch,
|
|
1531
|
+
stdout: deps.stdout,
|
|
1532
|
+
openApprovalUrl: deps.openUrl
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1307
1536
|
// src/tenant.ts
|
|
1308
1537
|
var import_apps = require("@odla-ai/apps");
|
|
1309
1538
|
function resolveEnv(cfg, requested) {
|
|
@@ -1409,7 +1638,7 @@ function errorMessage(body) {
|
|
|
1409
1638
|
}
|
|
1410
1639
|
|
|
1411
1640
|
// src/app-export.ts
|
|
1412
|
-
var
|
|
1641
|
+
var import_node_fs7 = require("fs");
|
|
1413
1642
|
var import_node_stream = require("stream");
|
|
1414
1643
|
var import_promises = require("stream/promises");
|
|
1415
1644
|
async function appExport(options) {
|
|
@@ -1442,7 +1671,7 @@ async function appExport(options) {
|
|
|
1442
1671
|
const download = await doFetch(`${base}/backups/${newest.id}/download`, { headers: auth });
|
|
1443
1672
|
if (!download.ok || !download.body) throw new Error(`download failed (${download.status})`);
|
|
1444
1673
|
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,
|
|
1674
|
+
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(download.body), (0, import_node_fs7.createWriteStream)(file));
|
|
1446
1675
|
if (options.json) out.log(JSON.stringify({ file, backup: newest }, null, 2));
|
|
1447
1676
|
else {
|
|
1448
1677
|
out.log(`${tenant}: wrote ${file} (${newest.bytes} bytes, ${newest.kind} snapshot at tx ${newest.max_tx})`);
|
|
@@ -1452,7 +1681,7 @@ async function appExport(options) {
|
|
|
1452
1681
|
}
|
|
1453
1682
|
|
|
1454
1683
|
// src/app-import.ts
|
|
1455
|
-
var
|
|
1684
|
+
var import_node_fs8 = require("fs");
|
|
1456
1685
|
var import_import = require("@odla-ai/db/import");
|
|
1457
1686
|
function chooseIdMode(options, rows) {
|
|
1458
1687
|
const chosen = [options.idField && "field", options.key && "key", options.generateIds && "generate"].filter(Boolean);
|
|
@@ -1471,7 +1700,7 @@ async function appImport(options) {
|
|
|
1471
1700
|
const say = options.json ? (line) => out.error(line) : (line) => out.log(line);
|
|
1472
1701
|
const doFetch = options.fetch ?? fetch;
|
|
1473
1702
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
1474
|
-
const text = options.file === "-" ? (options.readStdin ?? (() => (0,
|
|
1703
|
+
const text = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs8.readFileSync)(0, "utf8")))() : (0, import_node_fs8.readFileSync)(options.file, "utf8");
|
|
1475
1704
|
const { format, sources } = (0, import_import.parseImport)(text, options.ns);
|
|
1476
1705
|
if (format === "namespace-map" && options.ns) {
|
|
1477
1706
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2438,13 +2667,13 @@ function printGroup(out, heading, items) {
|
|
|
2438
2667
|
|
|
2439
2668
|
// src/doctor-checks.ts
|
|
2440
2669
|
var import_node_child_process3 = require("child_process");
|
|
2441
|
-
var
|
|
2442
|
-
var
|
|
2670
|
+
var import_node_fs10 = require("fs");
|
|
2671
|
+
var import_node_path8 = require("path");
|
|
2443
2672
|
|
|
2444
2673
|
// src/wrangler.ts
|
|
2445
2674
|
var import_node_child_process2 = require("child_process");
|
|
2446
|
-
var
|
|
2447
|
-
var
|
|
2675
|
+
var import_node_fs9 = require("fs");
|
|
2676
|
+
var import_node_path7 = require("path");
|
|
2448
2677
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
2449
2678
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
2450
2679
|
let stdout = "";
|
|
@@ -2458,15 +2687,15 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
2458
2687
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
2459
2688
|
function findWranglerConfig(rootDir) {
|
|
2460
2689
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
2461
|
-
const path = (0,
|
|
2462
|
-
if ((0,
|
|
2690
|
+
const path = (0, import_node_path7.join)(rootDir, name);
|
|
2691
|
+
if ((0, import_node_fs9.existsSync)(path)) return path;
|
|
2463
2692
|
}
|
|
2464
2693
|
return null;
|
|
2465
2694
|
}
|
|
2466
2695
|
function readWranglerConfig(path) {
|
|
2467
2696
|
if (path.endsWith(".toml")) return null;
|
|
2468
2697
|
try {
|
|
2469
|
-
return JSON.parse(stripJsonComments((0,
|
|
2698
|
+
return JSON.parse(stripJsonComments((0, import_node_fs9.readFileSync)(path, "utf8")));
|
|
2470
2699
|
} catch {
|
|
2471
2700
|
return null;
|
|
2472
2701
|
}
|
|
@@ -2564,10 +2793,10 @@ function wranglerWarnings(rootDir) {
|
|
|
2564
2793
|
for (const { label, block } of blocks) {
|
|
2565
2794
|
const assets = block.assets;
|
|
2566
2795
|
if (assets?.directory) {
|
|
2567
|
-
const dir = (0,
|
|
2568
|
-
if (dir === (0,
|
|
2796
|
+
const dir = (0, import_node_path8.resolve)(rootDir, assets.directory);
|
|
2797
|
+
if (dir === (0, import_node_path8.resolve)(rootDir)) {
|
|
2569
2798
|
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,
|
|
2799
|
+
} else if ((0, import_node_fs10.existsSync)((0, import_node_path8.join)(dir, "node_modules"))) {
|
|
2571
2800
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
2572
2801
|
}
|
|
2573
2802
|
}
|
|
@@ -2602,13 +2831,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
2602
2831
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
2603
2832
|
return warnings;
|
|
2604
2833
|
}
|
|
2605
|
-
const main = typeof config.main === "string" ? (0,
|
|
2606
|
-
if (!main || !(0,
|
|
2834
|
+
const main = typeof config.main === "string" ? (0, import_node_path8.resolve)(rootDir, config.main) : null;
|
|
2835
|
+
if (!main || !(0, import_node_fs10.existsSync)(main)) {
|
|
2607
2836
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
2608
2837
|
} else {
|
|
2609
2838
|
let source = "";
|
|
2610
2839
|
try {
|
|
2611
|
-
source = (0,
|
|
2840
|
+
source = (0, import_node_fs10.readFileSync)(main, "utf8");
|
|
2612
2841
|
} catch {
|
|
2613
2842
|
}
|
|
2614
2843
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -2632,7 +2861,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
2632
2861
|
}
|
|
2633
2862
|
function readPackageJson(rootDir) {
|
|
2634
2863
|
try {
|
|
2635
|
-
return JSON.parse((0,
|
|
2864
|
+
return JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path8.join)(rootDir, "package.json"), "utf8"));
|
|
2636
2865
|
} catch {
|
|
2637
2866
|
return null;
|
|
2638
2867
|
}
|
|
@@ -2859,13 +3088,13 @@ function harnessOption(value, flag) {
|
|
|
2859
3088
|
}
|
|
2860
3089
|
|
|
2861
3090
|
// src/init.ts
|
|
2862
|
-
var
|
|
2863
|
-
var
|
|
3091
|
+
var import_node_fs11 = require("fs");
|
|
3092
|
+
var import_node_path9 = require("path");
|
|
2864
3093
|
function initProject(options) {
|
|
2865
3094
|
const out = options.stdout ?? console;
|
|
2866
|
-
const rootDir = (0,
|
|
2867
|
-
const configPath = (0,
|
|
2868
|
-
if ((0,
|
|
3095
|
+
const rootDir = (0, import_node_path9.resolve)(options.rootDir ?? process.cwd());
|
|
3096
|
+
const configPath = (0, import_node_path9.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
3097
|
+
if ((0, import_node_fs11.existsSync)(configPath) && !options.force) {
|
|
2869
3098
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
2870
3099
|
}
|
|
2871
3100
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -2875,20 +3104,20 @@ function initProject(options) {
|
|
|
2875
3104
|
const services = options.services?.length ? options.services : ["db", "ai"];
|
|
2876
3105
|
if (services.includes("calendar") && !services.includes("db")) throw new Error("--services calendar requires db");
|
|
2877
3106
|
const aiProvider = options.aiProvider ?? "anthropic";
|
|
2878
|
-
(0,
|
|
2879
|
-
(0,
|
|
2880
|
-
(0,
|
|
2881
|
-
(0,
|
|
2882
|
-
writeIfMissing((0,
|
|
2883
|
-
writeIfMissing((0,
|
|
3107
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.dirname)(configPath), { recursive: true });
|
|
3108
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
3109
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path9.resolve)(rootDir, ".odla"), { recursive: true });
|
|
3110
|
+
(0, import_node_fs11.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
3111
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
3112
|
+
writeIfMissing((0, import_node_path9.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
2884
3113
|
ensureGitignore(rootDir);
|
|
2885
3114
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
2886
3115
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
2887
3116
|
out.log("updated .gitignore for local odla credentials");
|
|
2888
3117
|
}
|
|
2889
3118
|
function writeIfMissing(path, text) {
|
|
2890
|
-
if ((0,
|
|
2891
|
-
(0,
|
|
3119
|
+
if ((0, import_node_fs11.existsSync)(path)) return;
|
|
3120
|
+
(0, import_node_fs11.writeFileSync)(path, text);
|
|
2892
3121
|
}
|
|
2893
3122
|
function configTemplate(input) {
|
|
2894
3123
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -3119,9 +3348,9 @@ async function resolveVaultWrite(options) {
|
|
|
3119
3348
|
}
|
|
3120
3349
|
|
|
3121
3350
|
// src/skill.ts
|
|
3122
|
-
var
|
|
3123
|
-
var
|
|
3124
|
-
var
|
|
3351
|
+
var import_node_fs12 = require("fs");
|
|
3352
|
+
var import_node_os2 = require("os");
|
|
3353
|
+
var import_node_path10 = require("path");
|
|
3125
3354
|
var import_node_url2 = require("url");
|
|
3126
3355
|
|
|
3127
3356
|
// src/skill-adapters.ts
|
|
@@ -3200,8 +3429,8 @@ function installSkill(options = {}) {
|
|
|
3200
3429
|
const files = listFiles(sourceDir);
|
|
3201
3430
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
3202
3431
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
3203
|
-
const root = (0,
|
|
3204
|
-
const home = (0,
|
|
3432
|
+
const root = (0, import_node_path10.resolve)(options.dir ?? process.cwd());
|
|
3433
|
+
const home = (0, import_node_path10.resolve)(options.homeDir ?? (0, import_node_os2.homedir)());
|
|
3205
3434
|
const plans = /* @__PURE__ */ new Map();
|
|
3206
3435
|
const targets = /* @__PURE__ */ new Map();
|
|
3207
3436
|
const rememberTarget = (harness, target) => {
|
|
@@ -3215,48 +3444,48 @@ function installSkill(options = {}) {
|
|
|
3215
3444
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
3216
3445
|
};
|
|
3217
3446
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
3218
|
-
for (const rel of files) plan((0,
|
|
3447
|
+
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
3448
|
};
|
|
3220
3449
|
let targetDir;
|
|
3221
3450
|
if (options.global) {
|
|
3222
|
-
const claudeRoot = (0,
|
|
3223
|
-
const codexRoot = (0,
|
|
3451
|
+
const claudeRoot = (0, import_node_path10.join)(home, ".claude", "skills");
|
|
3452
|
+
const codexRoot = (0, import_node_path10.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path10.join)(home, ".codex"), "skills");
|
|
3224
3453
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
3225
3454
|
for (const harness of harnesses) {
|
|
3226
3455
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
3227
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
3456
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path10.dirname)((0, import_node_path10.dirname)(codexRoot)));
|
|
3228
3457
|
rememberTarget(harness, skillRoot);
|
|
3229
3458
|
}
|
|
3230
3459
|
} else {
|
|
3231
|
-
const sharedRoot = (0,
|
|
3460
|
+
const sharedRoot = (0, import_node_path10.join)(root, ".agents", "skills");
|
|
3232
3461
|
planSkillTree(sharedRoot);
|
|
3233
|
-
const claudeRoot = (0,
|
|
3462
|
+
const claudeRoot = (0, import_node_path10.join)(root, ".claude", "skills");
|
|
3234
3463
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
3235
3464
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
3236
3465
|
if (harnesses.includes("claude")) {
|
|
3237
3466
|
for (const skill of skillNames(files)) {
|
|
3238
|
-
const canonical = (0,
|
|
3239
|
-
plan((0,
|
|
3467
|
+
const canonical = (0, import_node_fs12.readFileSync)((0, import_node_path10.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
3468
|
+
plan((0, import_node_path10.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical));
|
|
3240
3469
|
}
|
|
3241
3470
|
rememberTarget("claude", claudeRoot);
|
|
3242
3471
|
}
|
|
3243
3472
|
if (harnesses.includes("cursor")) {
|
|
3244
|
-
const cursorRule = (0,
|
|
3473
|
+
const cursorRule = (0, import_node_path10.join)(root, ".cursor", "rules", "odla.mdc");
|
|
3245
3474
|
plan(cursorRule, CURSOR_RULE);
|
|
3246
3475
|
rememberTarget("cursor", cursorRule);
|
|
3247
3476
|
}
|
|
3248
3477
|
if (harnesses.includes("agents")) {
|
|
3249
|
-
const agentsFile = (0,
|
|
3478
|
+
const agentsFile = (0, import_node_path10.join)(root, "AGENTS.md");
|
|
3250
3479
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3251
3480
|
rememberTarget("agents", agentsFile);
|
|
3252
3481
|
}
|
|
3253
3482
|
if (harnesses.includes("copilot")) {
|
|
3254
|
-
const copilotFile = (0,
|
|
3483
|
+
const copilotFile = (0, import_node_path10.join)(root, ".github", "copilot-instructions.md");
|
|
3255
3484
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3256
3485
|
rememberTarget("copilot", copilotFile);
|
|
3257
3486
|
}
|
|
3258
3487
|
if (harnesses.includes("gemini")) {
|
|
3259
|
-
const geminiFile = (0,
|
|
3488
|
+
const geminiFile = (0, import_node_path10.join)(root, "GEMINI.md");
|
|
3260
3489
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
3261
3490
|
rememberTarget("gemini", geminiFile);
|
|
3262
3491
|
}
|
|
@@ -3270,11 +3499,11 @@ function installSkill(options = {}) {
|
|
|
3270
3499
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
3271
3500
|
continue;
|
|
3272
3501
|
}
|
|
3273
|
-
if (!(0,
|
|
3502
|
+
if (!(0, import_node_fs12.existsSync)(file.target)) {
|
|
3274
3503
|
writtenPaths.add(file.target);
|
|
3275
3504
|
continue;
|
|
3276
3505
|
}
|
|
3277
|
-
const current = (0,
|
|
3506
|
+
const current = (0, import_node_fs12.readFileSync)(file.target, "utf8");
|
|
3278
3507
|
if (current === file.content) {
|
|
3279
3508
|
unchangedPaths.add(file.target);
|
|
3280
3509
|
} else if (file.managedMerge || options.force) {
|
|
@@ -3291,9 +3520,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3291
3520
|
);
|
|
3292
3521
|
}
|
|
3293
3522
|
for (const file of plans.values()) {
|
|
3294
|
-
if (!(0,
|
|
3295
|
-
(0,
|
|
3296
|
-
(0,
|
|
3523
|
+
if (!(0, import_node_fs12.existsSync)(file.target) || (0, import_node_fs12.readFileSync)(file.target, "utf8") !== file.content) {
|
|
3524
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(file.target), { recursive: true });
|
|
3525
|
+
(0, import_node_fs12.writeFileSync)(file.target, file.content);
|
|
3297
3526
|
}
|
|
3298
3527
|
}
|
|
3299
3528
|
const skills = skillNames(files);
|
|
@@ -3312,7 +3541,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
3312
3541
|
};
|
|
3313
3542
|
}
|
|
3314
3543
|
function pathsUnder(root, paths) {
|
|
3315
|
-
return [...paths].map((path) => (0,
|
|
3544
|
+
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
3545
|
}
|
|
3317
3546
|
function normalizeHarnesses(values, global) {
|
|
3318
3547
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -3334,9 +3563,9 @@ function normalizeHarnesses(values, global) {
|
|
|
3334
3563
|
function managedFileContent(path, block, force, boundary) {
|
|
3335
3564
|
const symlink = symlinkedComponent(boundary, path);
|
|
3336
3565
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
3337
|
-
if (!(0,
|
|
3566
|
+
if (!(0, import_node_fs12.existsSync)(path)) return `${block}
|
|
3338
3567
|
`;
|
|
3339
|
-
const current = (0,
|
|
3568
|
+
const current = (0, import_node_fs12.readFileSync)(path, "utf8");
|
|
3340
3569
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
3341
3570
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
3342
3571
|
const startAt = current.indexOf(start);
|
|
@@ -3357,15 +3586,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
3357
3586
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
3358
3587
|
}
|
|
3359
3588
|
function symlinkedComponent(boundary, target) {
|
|
3360
|
-
const rel = (0,
|
|
3361
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
3589
|
+
const rel = (0, import_node_path10.relative)(boundary, target);
|
|
3590
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path10.sep}`) || (0, import_node_path10.isAbsolute)(rel)) {
|
|
3362
3591
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
3363
3592
|
}
|
|
3364
3593
|
let current = boundary;
|
|
3365
|
-
for (const part of rel.split(
|
|
3366
|
-
current = (0,
|
|
3594
|
+
for (const part of rel.split(import_node_path10.sep).filter(Boolean)) {
|
|
3595
|
+
current = (0, import_node_path10.join)(current, part);
|
|
3367
3596
|
try {
|
|
3368
|
-
if ((0,
|
|
3597
|
+
if ((0, import_node_fs12.lstatSync)(current).isSymbolicLink()) return current;
|
|
3369
3598
|
} catch (error) {
|
|
3370
3599
|
if (error.code !== "ENOENT") throw error;
|
|
3371
3600
|
}
|
|
@@ -3376,13 +3605,13 @@ function skillNames(files) {
|
|
|
3376
3605
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
3377
3606
|
}
|
|
3378
3607
|
function listFiles(dir) {
|
|
3379
|
-
if (!(0,
|
|
3608
|
+
if (!(0, import_node_fs12.existsSync)(dir)) return [];
|
|
3380
3609
|
const results = [];
|
|
3381
3610
|
const walk = (current) => {
|
|
3382
|
-
for (const entry of (0,
|
|
3383
|
-
const path = (0,
|
|
3611
|
+
for (const entry of (0, import_node_fs12.readdirSync)(current, { withFileTypes: true })) {
|
|
3612
|
+
const path = (0, import_node_path10.join)(current, entry.name);
|
|
3384
3613
|
if (entry.isDirectory()) walk(path);
|
|
3385
|
-
else results.push((0,
|
|
3614
|
+
else results.push((0, import_node_path10.relative)(dir, path));
|
|
3386
3615
|
}
|
|
3387
3616
|
};
|
|
3388
3617
|
walk(dir);
|
|
@@ -3529,13 +3758,16 @@ async function safeText3(res) {
|
|
|
3529
3758
|
|
|
3530
3759
|
// src/cli-project.ts
|
|
3531
3760
|
var SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
3532
|
-
function install(parsed, positionals) {
|
|
3761
|
+
function install(parsed, positionals, deps) {
|
|
3533
3762
|
assertArgs(parsed, SKILL_OPTS, positionals);
|
|
3534
3763
|
installSkill({
|
|
3535
3764
|
dir: stringOpt(parsed.options.dir),
|
|
3536
3765
|
global: parsed.options.global === true,
|
|
3537
3766
|
harnesses: harnessList(parsed),
|
|
3538
|
-
force: parsed.options.force === true
|
|
3767
|
+
force: parsed.options.force === true,
|
|
3768
|
+
homeDir: deps.homeDir,
|
|
3769
|
+
codexHomeDir: deps.codexHomeDir,
|
|
3770
|
+
stdout: deps.stdout
|
|
3539
3771
|
});
|
|
3540
3772
|
}
|
|
3541
3773
|
async function secretsCommand(parsed, deps) {
|
|
@@ -3551,6 +3783,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
3551
3783
|
token: stringOpt(parsed.options.token),
|
|
3552
3784
|
email: stringOpt(parsed.options.email),
|
|
3553
3785
|
yes: parsed.options.yes === true,
|
|
3786
|
+
readStdin: deps.readStdin,
|
|
3554
3787
|
fetch: deps.fetch,
|
|
3555
3788
|
stdout: deps.stdout
|
|
3556
3789
|
};
|
|
@@ -3609,8 +3842,8 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3609
3842
|
return true;
|
|
3610
3843
|
}
|
|
3611
3844
|
if (command === "setup") {
|
|
3612
|
-
install(parsed, 1);
|
|
3613
|
-
console.log(
|
|
3845
|
+
install(parsed, 1, deps);
|
|
3846
|
+
(deps.stdout ?? console).log(
|
|
3614
3847
|
"\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
3848
|
);
|
|
3616
3849
|
return true;
|
|
@@ -3618,7 +3851,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3618
3851
|
if (command === "skill") {
|
|
3619
3852
|
const sub = parsed.positionals[1];
|
|
3620
3853
|
if (sub !== "install") throw new Error(`unknown skill subcommand "${sub ?? ""}". Try "odla-ai skill install".`);
|
|
3621
|
-
install(parsed, 2);
|
|
3854
|
+
install(parsed, 2, deps);
|
|
3622
3855
|
return true;
|
|
3623
3856
|
}
|
|
3624
3857
|
if (command === "secrets") {
|
|
@@ -3629,9 +3862,9 @@ async function projectCommand(command, parsed, deps) {
|
|
|
3629
3862
|
}
|
|
3630
3863
|
|
|
3631
3864
|
// src/code-connect.ts
|
|
3632
|
-
var
|
|
3633
|
-
var
|
|
3634
|
-
var
|
|
3865
|
+
var import_node_fs14 = require("fs");
|
|
3866
|
+
var import_node_os4 = require("os");
|
|
3867
|
+
var import_node_path12 = require("path");
|
|
3635
3868
|
|
|
3636
3869
|
// ../harness/dist/chunk-QTUEF2HZ.js
|
|
3637
3870
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -6516,9 +6749,9 @@ var CodePiRuntimeEngine = class {
|
|
|
6516
6749
|
};
|
|
6517
6750
|
|
|
6518
6751
|
// src/help.ts
|
|
6519
|
-
var
|
|
6752
|
+
var import_node_fs13 = require("fs");
|
|
6520
6753
|
function cliVersion() {
|
|
6521
|
-
const pkg = JSON.parse((0,
|
|
6754
|
+
const pkg = JSON.parse((0, import_node_fs13.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
|
|
6522
6755
|
return pkg.version ?? "unknown";
|
|
6523
6756
|
}
|
|
6524
6757
|
function printHelp(output = console) {
|
|
@@ -6553,11 +6786,21 @@ Usage:
|
|
|
6553
6786
|
odla-ai app owners list [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
6554
6787
|
odla-ai app owners add <email> [--email <odla-account>] [--json]
|
|
6555
6788
|
odla-ai app owners remove <email> [--email <odla-account>] [--json]
|
|
6556
|
-
odla-ai pm
|
|
6557
|
-
odla-ai pm
|
|
6789
|
+
odla-ai pm goal list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6790
|
+
odla-ai pm task list [--app <id>] [--column <c>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6791
|
+
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6792
|
+
odla-ai pm bug list [--app <id>] [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
6793
|
+
odla-ai pm goal add --app <id> --title <t> [--status <s>] [--proof <text>] [--target <pct>] [--mutation-id <id>] [--json]
|
|
6794
|
+
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]
|
|
6795
|
+
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
6796
|
+
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]
|
|
6558
6797
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
6559
|
-
odla-ai pm
|
|
6560
|
-
odla-ai pm
|
|
6798
|
+
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
6799
|
+
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]
|
|
6800
|
+
odla-ai pm decision set <id> [--title <t>|--status <s>|--body <text>] [--mutation-id <id>] [--json]
|
|
6801
|
+
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]
|
|
6802
|
+
odla-ai pm <goal|task|decision> done <id> [--mutation-id <id>]
|
|
6803
|
+
odla-ai pm bug done <id> [--decision <accepted-decision-id>] [--mutation-id <id>]
|
|
6561
6804
|
odla-ai pm <goal|task|decision|bug> comment <id> --body "..." [--mutation-id <id>]
|
|
6562
6805
|
odla-ai pm <goal|task|decision|bug> comments <id> [--json]
|
|
6563
6806
|
odla-ai pm <goal|task|decision|bug> rm <id>
|
|
@@ -6596,15 +6839,16 @@ Usage:
|
|
|
6596
6839
|
odla-ai runbook rm <slug> [--app <id>]
|
|
6597
6840
|
odla-ai capabilities [--json]
|
|
6598
6841
|
odla-ai code connect [--env dev|prod] [--email <odla-account>] [--engine auto|container|podman|docker] [--slots <1-64>] [--once]
|
|
6599
|
-
odla-ai admin ai show [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6600
|
-
odla-ai admin ai models [--provider <id>] [--json]
|
|
6601
|
-
odla-ai admin ai set <purpose> [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
odla-ai admin ai
|
|
6606
|
-
odla-ai admin ai
|
|
6607
|
-
odla-ai admin ai
|
|
6842
|
+
odla-ai admin ai show [--context <name>] [--platform https://odla.ai] [--email <odla-account>] [--json]
|
|
6843
|
+
odla-ai admin ai models [--context <name>] [--provider <id>] [--json]
|
|
6844
|
+
odla-ai admin ai set <purpose> [--context <name>] [--provider <id>] [--model <id>] [--enabled|--no-enabled]
|
|
6845
|
+
[--max-input-bytes <n>] [--max-output-tokens <n>] [--max-calls-per-run <n>] [--json]
|
|
6846
|
+
odla-ai admin ai set security [--context <name>] --discovery-provider <id> --discovery-model <id>
|
|
6847
|
+
--validation-provider <id> --validation-model <id> [--enabled|--no-enabled] [--json]
|
|
6848
|
+
odla-ai admin ai credentials [--context <name>] [--json]
|
|
6849
|
+
odla-ai admin ai credential set <provider> [--context <name>] (--from-env <NAME>|--stdin)
|
|
6850
|
+
odla-ai admin ai usage [--context <name>] [--app-id <id>] [--env <env>] [--run-id <id>] [--limit <1-500>] [--json]
|
|
6851
|
+
odla-ai admin ai audit [--context <name>] [--limit <1-200>] [--json]
|
|
6608
6852
|
odla-ai security github connect [--repo owner/name] [--env dev] [--email <odla-account>] [--no-open]
|
|
6609
6853
|
odla-ai security github disconnect --source <id> [--env dev] [--yes]
|
|
6610
6854
|
odla-ai security plan [--env dev] [--json]
|
|
@@ -7031,8 +7275,8 @@ function digestText(value) {
|
|
|
7031
7275
|
var import_node_child_process6 = require("child_process");
|
|
7032
7276
|
var import_node_crypto2 = require("crypto");
|
|
7033
7277
|
var import_promises10 = require("fs/promises");
|
|
7034
|
-
var
|
|
7035
|
-
var
|
|
7278
|
+
var import_node_os3 = require("os");
|
|
7279
|
+
var import_node_path11 = require("path");
|
|
7036
7280
|
var import_node_url3 = require("url");
|
|
7037
7281
|
|
|
7038
7282
|
// src/code-runtime-config.ts
|
|
@@ -7118,10 +7362,10 @@ async function embeddedPiImageName() {
|
|
|
7118
7362
|
return `odla-ai/pi-agent:embedded-sha256-${(0, import_node_crypto2.createHash)("sha256").update(bundle).digest("hex")}`;
|
|
7119
7363
|
}
|
|
7120
7364
|
async function buildEmbeddedPiImage(engine, image, run) {
|
|
7121
|
-
const context = await (0, import_promises10.mkdtemp)((0,
|
|
7365
|
+
const context = await (0, import_promises10.mkdtemp)((0, import_node_path11.join)((0, import_node_os3.tmpdir)(), "odla-code-pi-"));
|
|
7122
7366
|
try {
|
|
7123
|
-
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0,
|
|
7124
|
-
await (0, import_promises10.writeFile)((0,
|
|
7367
|
+
await (0, import_promises10.copyFile)(embeddedPiAssetPath(), (0, import_node_path11.join)(context, "pi-agent.js"));
|
|
7368
|
+
await (0, import_promises10.writeFile)((0, import_node_path11.join)(context, "Dockerfile"), [
|
|
7125
7369
|
`FROM ${CODE_NODE_IMAGE}`,
|
|
7126
7370
|
"COPY pi-agent.js /opt/odla/pi-agent.js",
|
|
7127
7371
|
"WORKDIR /workspace",
|
|
@@ -7137,8 +7381,8 @@ async function buildEmbeddedPiImage(engine, image, run) {
|
|
|
7137
7381
|
// src/code-connect.ts
|
|
7138
7382
|
async function codeConnect(options) {
|
|
7139
7383
|
const cwd = options.cwd ?? process.cwd();
|
|
7140
|
-
const configPath = (0,
|
|
7141
|
-
const cfg = (0,
|
|
7384
|
+
const configPath = (0, import_node_path12.resolve)(cwd, options.configPath);
|
|
7385
|
+
const cfg = (0, import_node_fs14.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
7142
7386
|
const requestedAppId = options.appId?.trim();
|
|
7143
7387
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
7144
7388
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -7172,7 +7416,7 @@ async function codeConnect(options) {
|
|
|
7172
7416
|
);
|
|
7173
7417
|
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");
|
|
7174
7418
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
7175
|
-
const hostName = (options.name ?? (0,
|
|
7419
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
7176
7420
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
7177
7421
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
7178
7422
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -7209,8 +7453,8 @@ async function codeConnect(options) {
|
|
|
7209
7453
|
platform: hostPlatform,
|
|
7210
7454
|
arch: process.arch,
|
|
7211
7455
|
engines: [engine],
|
|
7212
|
-
cpuCount: (0,
|
|
7213
|
-
memoryBytes: (0,
|
|
7456
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
7457
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
7214
7458
|
source: descriptor2,
|
|
7215
7459
|
images: {
|
|
7216
7460
|
ready: true,
|
|
@@ -7356,246 +7600,19 @@ async function codeCommand(parsed, dependencies) {
|
|
|
7356
7600
|
}
|
|
7357
7601
|
|
|
7358
7602
|
// src/operator-credentials.ts
|
|
7359
|
-
var
|
|
7603
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
7360
7604
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
7361
7605
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
7362
7606
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
7363
|
-
const source =
|
|
7607
|
+
const source = clean3(
|
|
7364
7608
|
stringOpt(parsed.options.token)
|
|
7365
|
-
) ? "flag" :
|
|
7609
|
+
) ? "flag" : clean3(import_node_process9.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
7366
7610
|
return {
|
|
7367
7611
|
source,
|
|
7368
7612
|
cacheFile: context.cfg.local.tokenFile,
|
|
7369
7613
|
cacheStatus
|
|
7370
7614
|
};
|
|
7371
7615
|
}
|
|
7372
|
-
function clean(value) {
|
|
7373
|
-
const normalized = value?.trim();
|
|
7374
|
-
return normalized || void 0;
|
|
7375
|
-
}
|
|
7376
|
-
|
|
7377
|
-
// src/operator-context.ts
|
|
7378
|
-
var import_node_fs14 = require("fs");
|
|
7379
|
-
var import_node_path12 = require("path");
|
|
7380
|
-
var import_node_process9 = __toESM(require("process"), 1);
|
|
7381
|
-
|
|
7382
|
-
// src/operator-profiles.ts
|
|
7383
|
-
var import_node_fs13 = require("fs");
|
|
7384
|
-
var import_node_os4 = require("os");
|
|
7385
|
-
var import_node_path11 = require("path");
|
|
7386
|
-
var import_node_process8 = __toESM(require("process"), 1);
|
|
7387
|
-
function operatorProfileFile() {
|
|
7388
|
-
return (0, import_node_path11.resolve)(
|
|
7389
|
-
clean2(import_node_process8.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".odla", "contexts.json")
|
|
7390
|
-
);
|
|
7391
|
-
}
|
|
7392
|
-
function resolveOperatorProfile(parsed) {
|
|
7393
|
-
const fromFlag = clean2(stringOpt(parsed.options.context));
|
|
7394
|
-
const fromEnvironment = clean2(import_node_process8.default.env.ODLA_CONTEXT);
|
|
7395
|
-
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7396
|
-
const file = operatorProfileFile();
|
|
7397
|
-
if (!name) {
|
|
7398
|
-
return { name: null, source: "unresolved", file, value: null };
|
|
7399
|
-
}
|
|
7400
|
-
assertOperatorName(name, "context");
|
|
7401
|
-
const profiles = readOperatorProfiles(file);
|
|
7402
|
-
const value = Object.hasOwn(profiles, name) ? profiles[name] : void 0;
|
|
7403
|
-
if (!value) {
|
|
7404
|
-
throw new Error(
|
|
7405
|
-
`operator context "${name}" not found in ${file}; run "odla-ai context list" or save it first`
|
|
7406
|
-
);
|
|
7407
|
-
}
|
|
7408
|
-
return {
|
|
7409
|
-
name,
|
|
7410
|
-
source: fromFlag ? "flag" : "environment",
|
|
7411
|
-
file,
|
|
7412
|
-
value
|
|
7413
|
-
};
|
|
7414
|
-
}
|
|
7415
|
-
function listOperatorProfiles(file = operatorProfileFile()) {
|
|
7416
|
-
return Object.entries(readOperatorProfiles(file)).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({ name, ...profile }));
|
|
7417
|
-
}
|
|
7418
|
-
function saveOperatorProfile(name, profile, file = operatorProfileFile()) {
|
|
7419
|
-
assertOperatorName(name, "context");
|
|
7420
|
-
const normalized = validateProfile(profile, `operator context "${name}"`);
|
|
7421
|
-
const profiles = readOperatorProfiles(file);
|
|
7422
|
-
profiles[name] = normalized;
|
|
7423
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7424
|
-
}
|
|
7425
|
-
function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
7426
|
-
assertOperatorName(name, "context");
|
|
7427
|
-
const profiles = readOperatorProfiles(file);
|
|
7428
|
-
if (!Object.hasOwn(profiles, name)) return false;
|
|
7429
|
-
delete profiles[name];
|
|
7430
|
-
writePrivateJson(file, { schemaVersion: 1, profiles });
|
|
7431
|
-
return true;
|
|
7432
|
-
}
|
|
7433
|
-
function operatorCredentialFiles(selection) {
|
|
7434
|
-
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");
|
|
7435
|
-
return {
|
|
7436
|
-
developer: (0, import_node_path11.join)(base, "dev-token.json"),
|
|
7437
|
-
scoped: (0, import_node_path11.join)(base, "admin-token.local.json")
|
|
7438
|
-
};
|
|
7439
|
-
}
|
|
7440
|
-
function assertOperatorName(value, label) {
|
|
7441
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(value)) {
|
|
7442
|
-
throw new Error(
|
|
7443
|
-
`${label} must contain lowercase letters, numbers, and hyphens`
|
|
7444
|
-
);
|
|
7445
|
-
}
|
|
7446
|
-
}
|
|
7447
|
-
function readOperatorProfiles(file) {
|
|
7448
|
-
if (!(0, import_node_fs13.existsSync)(file)) return emptyProfiles();
|
|
7449
|
-
let raw;
|
|
7450
|
-
try {
|
|
7451
|
-
raw = JSON.parse((0, import_node_fs13.readFileSync)(file, "utf8"));
|
|
7452
|
-
} catch {
|
|
7453
|
-
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
7454
|
-
}
|
|
7455
|
-
if (!raw || typeof raw !== "object" || raw.schemaVersion !== 1 || !raw.profiles || typeof raw.profiles !== "object" || Array.isArray(raw.profiles)) {
|
|
7456
|
-
throw new Error(`operator context file ${file} has an invalid shape`);
|
|
7457
|
-
}
|
|
7458
|
-
const unknown = Object.keys(raw).filter(
|
|
7459
|
-
(key) => !["schemaVersion", "profiles"].includes(key)
|
|
7460
|
-
);
|
|
7461
|
-
if (unknown.length > 0) {
|
|
7462
|
-
throw new Error(
|
|
7463
|
-
`operator context file ${file} contains unsupported field(s): ${unknown.sort().join(", ")}`
|
|
7464
|
-
);
|
|
7465
|
-
}
|
|
7466
|
-
const profiles = emptyProfiles();
|
|
7467
|
-
for (const [name, value] of Object.entries(
|
|
7468
|
-
raw.profiles
|
|
7469
|
-
)) {
|
|
7470
|
-
assertOperatorName(name, "context");
|
|
7471
|
-
profiles[name] = validateProfile(value, `operator context "${name}"`);
|
|
7472
|
-
}
|
|
7473
|
-
return profiles;
|
|
7474
|
-
}
|
|
7475
|
-
function emptyProfiles() {
|
|
7476
|
-
return /* @__PURE__ */ Object.create(null);
|
|
7477
|
-
}
|
|
7478
|
-
function validateProfile(value, label) {
|
|
7479
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7480
|
-
throw new Error(`${label} has an invalid shape`);
|
|
7481
|
-
}
|
|
7482
|
-
const unknown = Object.keys(value).filter(
|
|
7483
|
-
(key) => !["platform", "app", "environment"].includes(key)
|
|
7484
|
-
);
|
|
7485
|
-
if (unknown.length > 0) {
|
|
7486
|
-
throw new Error(
|
|
7487
|
-
`${label} contains unsupported field(s): ${unknown.sort().join(", ")}; contexts store scope metadata only, never credentials`
|
|
7488
|
-
);
|
|
7489
|
-
}
|
|
7490
|
-
const candidate = value;
|
|
7491
|
-
if (typeof candidate.platform !== "string") {
|
|
7492
|
-
throw new Error(`${label} needs an absolute platform URL`);
|
|
7493
|
-
}
|
|
7494
|
-
const platform = platformAudience(candidate.platform);
|
|
7495
|
-
const app = clean2(candidate.app);
|
|
7496
|
-
const environment2 = clean2(candidate.environment);
|
|
7497
|
-
if (app) assertOperatorName(app, "app");
|
|
7498
|
-
if (environment2) assertOperatorName(environment2, "environment");
|
|
7499
|
-
return {
|
|
7500
|
-
platform,
|
|
7501
|
-
...app ? { app } : {},
|
|
7502
|
-
...environment2 ? { environment: environment2 } : {}
|
|
7503
|
-
};
|
|
7504
|
-
}
|
|
7505
|
-
function clean2(value) {
|
|
7506
|
-
const normalized = value?.trim();
|
|
7507
|
-
return normalized || void 0;
|
|
7508
|
-
}
|
|
7509
|
-
|
|
7510
|
-
// src/operator-context.ts
|
|
7511
|
-
var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
7512
|
-
async function resolveOperatorContext(parsed, options = {}) {
|
|
7513
|
-
const profile = resolveOperatorProfile(parsed);
|
|
7514
|
-
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
7515
|
-
const configPath = (0, import_node_path12.resolve)(configArgument);
|
|
7516
|
-
const explicitConfig = parsed.options.config !== void 0;
|
|
7517
|
-
const hasConfig = (0, import_node_fs14.existsSync)(configPath);
|
|
7518
|
-
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
7519
|
-
await loadProjectConfig(configArgument);
|
|
7520
|
-
}
|
|
7521
|
-
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
7522
|
-
const platformFlag = clean3(stringOpt(parsed.options.platform));
|
|
7523
|
-
const platformEnvironment = clean3(import_node_process9.default.env.ODLA_PLATFORM_URL);
|
|
7524
|
-
const platformValue = platformAudience(
|
|
7525
|
-
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
7526
|
-
);
|
|
7527
|
-
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
7528
|
-
const appFlag = clean3(stringOpt(parsed.options.app));
|
|
7529
|
-
const appEnvironment = clean3(import_node_process9.default.env.ODLA_APP_ID);
|
|
7530
|
-
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
7531
|
-
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
7532
|
-
if (appValue) assertOperatorName(appValue, "app");
|
|
7533
|
-
if (options.requireApp && !appValue) {
|
|
7534
|
-
throw new Error(
|
|
7535
|
-
"app context is unresolved; pass --app <id>, set ODLA_APP_ID, select --context <name>, or run inside a project with odla.config.mjs"
|
|
7536
|
-
);
|
|
7537
|
-
}
|
|
7538
|
-
const envFlag = clean3(stringOpt(parsed.options.env));
|
|
7539
|
-
const envEnvironment = clean3(import_node_process9.default.env.ODLA_ENV);
|
|
7540
|
-
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
7541
|
-
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
7542
|
-
if (environmentValue) {
|
|
7543
|
-
assertOperatorName(environmentValue, "environment");
|
|
7544
|
-
}
|
|
7545
|
-
const rootDir = loaded?.rootDir ?? import_node_process9.default.cwd();
|
|
7546
|
-
const profileCredentials = operatorCredentialFiles(profile);
|
|
7547
|
-
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;
|
|
7548
|
-
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;
|
|
7549
|
-
const cfg = loaded ? {
|
|
7550
|
-
...loaded,
|
|
7551
|
-
platformUrl: platformValue,
|
|
7552
|
-
app: appValue ? {
|
|
7553
|
-
id: appValue,
|
|
7554
|
-
name: appValue === loaded.app.id ? loaded.app.name : appValue
|
|
7555
|
-
} : loaded.app,
|
|
7556
|
-
local: { ...loaded.local, tokenFile }
|
|
7557
|
-
} : {
|
|
7558
|
-
configPath,
|
|
7559
|
-
rootDir,
|
|
7560
|
-
platformUrl: platformValue,
|
|
7561
|
-
dbEndpoint: platformValue,
|
|
7562
|
-
app: {
|
|
7563
|
-
id: appValue ?? "operator",
|
|
7564
|
-
name: appValue ?? "Operator"
|
|
7565
|
-
},
|
|
7566
|
-
envs: environmentValue ? [environmentValue] : [],
|
|
7567
|
-
services: [],
|
|
7568
|
-
local: {
|
|
7569
|
-
tokenFile,
|
|
7570
|
-
credentialsFile: (0, import_node_path12.join)(rootDir, ".odla", "credentials.local.json"),
|
|
7571
|
-
devVarsFile: (0, import_node_path12.join)(rootDir, ".dev.vars"),
|
|
7572
|
-
gitignore: true
|
|
7573
|
-
}
|
|
7574
|
-
};
|
|
7575
|
-
return {
|
|
7576
|
-
cfg,
|
|
7577
|
-
profile: {
|
|
7578
|
-
name: profile.name,
|
|
7579
|
-
source: profile.source,
|
|
7580
|
-
file: profile.file
|
|
7581
|
-
},
|
|
7582
|
-
config: {
|
|
7583
|
-
path: configPath,
|
|
7584
|
-
status: loaded ? "loaded" : "absent",
|
|
7585
|
-
explicit: explicitConfig
|
|
7586
|
-
},
|
|
7587
|
-
platform: { value: platformValue, source: platformSource },
|
|
7588
|
-
app: { value: appValue, source: appSource },
|
|
7589
|
-
environment: {
|
|
7590
|
-
value: environmentValue,
|
|
7591
|
-
source: environmentSource
|
|
7592
|
-
},
|
|
7593
|
-
credentials: {
|
|
7594
|
-
developerTokenFile: tokenFile,
|
|
7595
|
-
scopedTokenFile
|
|
7596
|
-
}
|
|
7597
|
-
};
|
|
7598
|
-
}
|
|
7599
7616
|
function clean3(value) {
|
|
7600
7617
|
const normalized = value?.trim();
|
|
7601
7618
|
return normalized || void 0;
|
|
@@ -8439,33 +8456,53 @@ var ALIASES = {
|
|
|
8439
8456
|
decision: "decision",
|
|
8440
8457
|
bug: "bug"
|
|
8441
8458
|
};
|
|
8442
|
-
var
|
|
8443
|
-
|
|
8444
|
-
"
|
|
8445
|
-
"
|
|
8446
|
-
|
|
8447
|
-
"
|
|
8448
|
-
"
|
|
8449
|
-
"
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
]
|
|
8459
|
+
var COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
8460
|
+
var ACTION_OPTIONS = {
|
|
8461
|
+
list: ["app", "q", "limit", "offset"],
|
|
8462
|
+
add: ["app", "title", "mutation-id"],
|
|
8463
|
+
get: [],
|
|
8464
|
+
set: ["mutation-id"],
|
|
8465
|
+
done: ["mutation-id"],
|
|
8466
|
+
comment: ["body", "mutation-id"],
|
|
8467
|
+
comments: [],
|
|
8468
|
+
rm: []
|
|
8469
|
+
};
|
|
8470
|
+
var ENTITY_OPTIONS = {
|
|
8471
|
+
goal: {
|
|
8472
|
+
list: ["status"],
|
|
8473
|
+
add: ["status", "proof", "target"],
|
|
8474
|
+
set: ["title", "status", "proof", "target"],
|
|
8475
|
+
done: []
|
|
8476
|
+
},
|
|
8477
|
+
task: {
|
|
8478
|
+
list: ["column", "goal", "assignee"],
|
|
8479
|
+
add: ["column", "goal", "assignee", "due", "description", "desc", "body"],
|
|
8480
|
+
set: ["title", "column", "rank", "goal", "assignee", "due", "description", "desc", "body"],
|
|
8481
|
+
done: []
|
|
8482
|
+
},
|
|
8483
|
+
decision: {
|
|
8484
|
+
list: ["status"],
|
|
8485
|
+
add: ["status", "body"],
|
|
8486
|
+
set: ["title", "status", "body"],
|
|
8487
|
+
done: []
|
|
8488
|
+
},
|
|
8489
|
+
bug: {
|
|
8490
|
+
list: ["status", "severity", "goal", "assignee", "decision"],
|
|
8491
|
+
add: ["status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
8492
|
+
set: ["title", "status", "severity", "goal", "assignee", "decision", "description", "desc", "body"],
|
|
8493
|
+
done: ["decision"]
|
|
8494
|
+
}
|
|
8495
|
+
};
|
|
8496
|
+
function canonicalAction(action) {
|
|
8497
|
+
if (action === "create") return "add";
|
|
8498
|
+
if (action === "update" || action === "status" || action === "move") return "set";
|
|
8499
|
+
if (action === "delete") return "rm";
|
|
8500
|
+
return action in ACTION_OPTIONS ? action : null;
|
|
8501
|
+
}
|
|
8502
|
+
function allowedOptions(entity, action) {
|
|
8503
|
+
const entityOptions = action === "list" || action === "add" || action === "set" || action === "done" ? ENTITY_OPTIONS[entity][action] : [];
|
|
8504
|
+
return [...COMMON_OPTIONS, ...ACTION_OPTIONS[action], ...entityOptions];
|
|
8505
|
+
}
|
|
8469
8506
|
function requireId2(id, action) {
|
|
8470
8507
|
if (!id) throw new Error(`"pm ... ${action}" needs an item id`);
|
|
8471
8508
|
return id;
|
|
@@ -8497,27 +8534,25 @@ async function buildContext2(parsed, deps) {
|
|
|
8497
8534
|
async function pmCommand(parsed, deps = {}) {
|
|
8498
8535
|
const word = parsed.positionals[1] ?? "";
|
|
8499
8536
|
if (word === "handoff") {
|
|
8500
|
-
assertArgs(parsed,
|
|
8537
|
+
assertArgs(parsed, [...COMMON_OPTIONS, "app"], 2);
|
|
8501
8538
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
8502
8539
|
}
|
|
8503
8540
|
const entity = ALIASES[word];
|
|
8504
8541
|
if (!entity) throw new Error(`unknown pm entity "${word}". Try "odla-ai pm bug list" (goal|task|decision|bug).`);
|
|
8505
|
-
const
|
|
8506
|
-
|
|
8542
|
+
const requestedAction = parsed.positionals[2] ?? "list";
|
|
8543
|
+
const action = canonicalAction(requestedAction);
|
|
8544
|
+
if (!action) throw new Error(`unknown pm action "${requestedAction}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
8545
|
+
assertArgs(parsed, allowedOptions(entity, action), 4);
|
|
8507
8546
|
const ctx = await buildContext2(parsed, deps);
|
|
8508
8547
|
const id = parsed.positionals[3];
|
|
8509
8548
|
switch (action) {
|
|
8510
8549
|
case "list":
|
|
8511
8550
|
return pmList(ctx, entity, parsed);
|
|
8512
8551
|
case "add":
|
|
8513
|
-
case "create":
|
|
8514
8552
|
return pmAdd(ctx, entity, parsed);
|
|
8515
8553
|
case "get":
|
|
8516
8554
|
return pmGet(ctx, entity, requireId2(id, action));
|
|
8517
8555
|
case "set":
|
|
8518
|
-
case "update":
|
|
8519
|
-
case "status":
|
|
8520
|
-
case "move":
|
|
8521
8556
|
return pmSet(ctx, entity, requireId2(id, action), parsed);
|
|
8522
8557
|
case "done":
|
|
8523
8558
|
return pmDone(ctx, entity, requireId2(id, action), parsed);
|
|
@@ -8526,10 +8561,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
8526
8561
|
case "comments":
|
|
8527
8562
|
return pmComments(ctx, entity, requireId2(id, action));
|
|
8528
8563
|
case "rm":
|
|
8529
|
-
case "delete":
|
|
8530
8564
|
return pmRemove(ctx, entity, requireId2(id, action));
|
|
8531
|
-
default:
|
|
8532
|
-
throw new Error(`unknown pm action "${action}". Try list|add|get|set|done|comment|comments|rm.`);
|
|
8533
8565
|
}
|
|
8534
8566
|
}
|
|
8535
8567
|
|
|
@@ -9491,7 +9523,7 @@ function recordInvocation(parsed) {
|
|
|
9491
9523
|
try {
|
|
9492
9524
|
const entry = {
|
|
9493
9525
|
path: invocationPath(parsed.positionals),
|
|
9494
|
-
options: Object.
|
|
9526
|
+
options: Object.entries(parsed.options).map(([name, value]) => value === false ? `no-${name}` : name).sort()
|
|
9495
9527
|
};
|
|
9496
9528
|
if (!entry.path.length) return;
|
|
9497
9529
|
(0, import_node_fs15.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
@@ -10264,7 +10296,7 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
10264
10296
|
}
|
|
10265
10297
|
|
|
10266
10298
|
// src/runbook-command.ts
|
|
10267
|
-
var
|
|
10299
|
+
var ALLOWED2 = [
|
|
10268
10300
|
"config",
|
|
10269
10301
|
"token",
|
|
10270
10302
|
"email",
|
|
@@ -10352,7 +10384,7 @@ async function buildContext3(parsed, deps, action) {
|
|
|
10352
10384
|
}
|
|
10353
10385
|
async function runbookCommand(parsed, deps = {}) {
|
|
10354
10386
|
const action = parsed.positionals[1] ?? "list";
|
|
10355
|
-
assertArgs(parsed,
|
|
10387
|
+
assertArgs(parsed, ALLOWED2, action === "ask" || action === "search" ? 64 : 4);
|
|
10356
10388
|
const ctx = await buildContext3(parsed, deps, action);
|
|
10357
10389
|
const slug = parsed.positionals[2];
|
|
10358
10390
|
switch (action) {
|
|
@@ -11181,7 +11213,7 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
11181
11213
|
return;
|
|
11182
11214
|
}
|
|
11183
11215
|
if (command === "admin") {
|
|
11184
|
-
await adminCommand(parsed);
|
|
11216
|
+
await adminCommand(parsed, runtime);
|
|
11185
11217
|
return;
|
|
11186
11218
|
}
|
|
11187
11219
|
if (command === "agent") {
|