@odla-ai/cli 0.32.1 → 0.33.0
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/dist/bin.cjs +396 -137
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-L6YTOTWU.js → chunk-YSQORU5J.js} +364 -133
- package/dist/chunk-YSQORU5J.js.map +1 -0
- package/dist/{cli-LYFPBGNH.js → cli-U436OLYW.js} +2 -2
- package/dist/index.cjs +365 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -2
- package/dist/index.d.ts +45 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-L6YTOTWU.js.map +0 -1
- /package/dist/{cli-LYFPBGNH.js.map → cli-U436OLYW.js.map} +0 -0
package/dist/bin.cjs
CHANGED
|
@@ -1268,6 +1268,117 @@ var init_ai_config_validation = __esm({
|
|
|
1268
1268
|
}
|
|
1269
1269
|
});
|
|
1270
1270
|
|
|
1271
|
+
// src/calendar-config.ts
|
|
1272
|
+
function calendarServiceConfig(cfg, env) {
|
|
1273
|
+
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
1274
|
+
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1275
|
+
const google = cfg.calendar?.google;
|
|
1276
|
+
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
1277
|
+
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1278
|
+
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1279
|
+
const availability = unique(configured.map((id) => id.trim()));
|
|
1280
|
+
return {
|
|
1281
|
+
provider: "google",
|
|
1282
|
+
access: "book",
|
|
1283
|
+
bookingCalendarId: google.bookingCalendar?.[env]?.trim() ?? availability[0],
|
|
1284
|
+
availabilityCalendars: availability
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function calendarBookingPageUrl(cfg, env) {
|
|
1288
|
+
const value2 = cfg.calendar?.google.bookingPageUrl?.[env];
|
|
1289
|
+
if (value2 === void 0 || value2 === null) return value2;
|
|
1290
|
+
return new URL(value2).toString();
|
|
1291
|
+
}
|
|
1292
|
+
function validateCalendarConfig(cfg, envs, services, path) {
|
|
1293
|
+
const enabled = services.includes("calendar");
|
|
1294
|
+
if (!cfg.calendar) {
|
|
1295
|
+
if (enabled) throw new Error(`${path}: calendar.google is required when services includes "calendar"`);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
if (!isRecord5(cfg.calendar)) throw new Error(`${path}: calendar must be an object`);
|
|
1299
|
+
assertOnly2(cfg.calendar, ["google"], `${path}: calendar`);
|
|
1300
|
+
if (!isRecord5(cfg.calendar.google)) throw new Error(`${path}: calendar.google must be an object`);
|
|
1301
|
+
const google = cfg.calendar.google;
|
|
1302
|
+
assertOnly2(
|
|
1303
|
+
google,
|
|
1304
|
+
["availabilityCalendars", "calendars", "bookingCalendar", "bookingPageUrl"],
|
|
1305
|
+
`${path}: calendar.google`
|
|
1306
|
+
);
|
|
1307
|
+
const availabilityKey = google.availabilityCalendars !== void 0 ? "availabilityCalendars" : google.calendars !== void 0 ? "calendars" : null;
|
|
1308
|
+
if (!availabilityKey || google.availabilityCalendars !== void 0 && google.calendars !== void 0) {
|
|
1309
|
+
throw new Error(`${path}: calendar.google requires exactly one of availabilityCalendars or calendars (legacy)`);
|
|
1310
|
+
}
|
|
1311
|
+
const availability = google[availabilityKey];
|
|
1312
|
+
if (!isRecord5(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1313
|
+
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1314
|
+
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1315
|
+
for (const env of envs) {
|
|
1316
|
+
const ids = availability[env];
|
|
1317
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1318
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
for (const [env, ids] of Object.entries(availability)) {
|
|
1322
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1323
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1324
|
+
}
|
|
1325
|
+
if (ids.length > 10) {
|
|
1326
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1327
|
+
}
|
|
1328
|
+
if (ids.some((id) => !safeText2(id, 1024))) {
|
|
1329
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
if (google.bookingCalendar !== void 0) {
|
|
1333
|
+
if (!isRecord5(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1334
|
+
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1335
|
+
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1336
|
+
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1337
|
+
if (!safeText2(value2, 1024)) {
|
|
1338
|
+
throw new Error(`${path}: calendar.google.bookingCalendar.${env} must be a calendar id`);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (google.bookingPageUrl !== void 0) {
|
|
1343
|
+
if (!isRecord5(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1344
|
+
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1345
|
+
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1346
|
+
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1347
|
+
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
1348
|
+
throw new Error(`${path}: calendar.google.bookingPageUrl.${env} must be an HTTPS URL without credentials or fragment`);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
function assertOnly2(value2, allowed, label) {
|
|
1354
|
+
const extra = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
1355
|
+
if (extra) throw new Error(`${label}.${extra} is not supported`);
|
|
1356
|
+
}
|
|
1357
|
+
function isRecord5(value2) {
|
|
1358
|
+
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1359
|
+
}
|
|
1360
|
+
function safeText2(value2, max) {
|
|
1361
|
+
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1362
|
+
}
|
|
1363
|
+
function safeHttpsUrl(value2) {
|
|
1364
|
+
if (typeof value2 !== "string" || value2.length > 2048) return false;
|
|
1365
|
+
try {
|
|
1366
|
+
const url = new URL(value2);
|
|
1367
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.hash;
|
|
1368
|
+
} catch {
|
|
1369
|
+
return false;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
function unique(values) {
|
|
1373
|
+
return [...new Set(values.filter(Boolean))];
|
|
1374
|
+
}
|
|
1375
|
+
var init_calendar_config = __esm({
|
|
1376
|
+
"src/calendar-config.ts"() {
|
|
1377
|
+
"use strict";
|
|
1378
|
+
init_cjs_shims();
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1271
1382
|
// src/integration-validation.ts
|
|
1272
1383
|
function validateIntegrations(cfg, path, defaultServices) {
|
|
1273
1384
|
if (cfg.integrations === void 0) return;
|
|
@@ -1275,36 +1386,60 @@ function validateIntegrations(cfg, path, defaultServices) {
|
|
|
1275
1386
|
const ids = /* @__PURE__ */ new Set();
|
|
1276
1387
|
for (const [index, integration] of cfg.integrations.entries()) {
|
|
1277
1388
|
const at = `${path}: integrations[${index}]`;
|
|
1278
|
-
if (!
|
|
1389
|
+
if (!isRecord6(integration)) throw new Error(`${at} must be an object`);
|
|
1279
1390
|
if (!validId(integration.id)) throw new Error(`${at}.id must be lowercase letters, numbers, and hyphens`);
|
|
1280
1391
|
if (ids.has(integration.id)) throw new Error(`${path}: duplicate integration id "${integration.id}"`);
|
|
1281
1392
|
ids.add(integration.id);
|
|
1282
|
-
if (!
|
|
1283
|
-
if (!
|
|
1284
|
-
if (integration.schema !== void 0 && (!
|
|
1393
|
+
if (!safeText3(integration.title, 200)) throw new Error(`${at}.title is required`);
|
|
1394
|
+
if (!safeText3(integration.npm, 200)) throw new Error(`${at}.npm is required`);
|
|
1395
|
+
if (integration.schema !== void 0 && (!isRecord6(integration.schema) || !isRecord6(integration.schema.entities))) {
|
|
1285
1396
|
throw new Error(`${at}.schema must contain an entities object`);
|
|
1286
1397
|
}
|
|
1287
|
-
if (integration.rules !== void 0 && !
|
|
1398
|
+
if (integration.rules !== void 0 && !isRecord6(integration.rules)) throw new Error(`${at}.rules must be an object`);
|
|
1288
1399
|
validateSeeds(integration, at);
|
|
1289
1400
|
validateProbes(integration, at);
|
|
1401
|
+
validateSecrets(integration.secrets, at);
|
|
1290
1402
|
}
|
|
1291
1403
|
const needsDb = cfg.integrations.some((integration) => integration.schema || integration.rules || integration.seeds?.length);
|
|
1292
|
-
const services =
|
|
1404
|
+
const services = unique2(cfg.services?.length ? cfg.services : defaultServices);
|
|
1293
1405
|
if (needsDb && !services.includes("db")) throw new Error(`${path}: schema/rules/seed integrations require the db service`);
|
|
1294
1406
|
}
|
|
1407
|
+
function validateSecrets(value2, at) {
|
|
1408
|
+
if (value2 === void 0) return;
|
|
1409
|
+
if (!Array.isArray(value2)) throw new Error(`${at}.secrets must be an array`);
|
|
1410
|
+
const names = /* @__PURE__ */ new Set();
|
|
1411
|
+
for (const [index, secret] of value2.entries()) {
|
|
1412
|
+
const sat = `${at}.secrets[${index}]`;
|
|
1413
|
+
if (!isRecord6(secret)) throw new Error(`${sat} must be an object`);
|
|
1414
|
+
if (typeof secret.name !== "string" || !SECRET_NAME.test(secret.name) || secret.name.length > 64) {
|
|
1415
|
+
throw new Error(`${sat}.name must be lowercase snake_case (optionally "$"-prefixed when reserved), e.g. "clerk_webhook_secret"`);
|
|
1416
|
+
}
|
|
1417
|
+
const dollar = secret.name.startsWith("$");
|
|
1418
|
+
if (dollar !== (secret.reserved === true)) {
|
|
1419
|
+
throw new Error(
|
|
1420
|
+
dollar ? `${sat}.name is "$"-prefixed, so it must also set reserved: true` : `${sat} sets reserved: true, so its name must be "$"-prefixed`
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
if (!safeText3(secret.description, 500)) throw new Error(`${sat}.description is required \u2014 it is what doctor and the docs show`);
|
|
1424
|
+
if (secret.pattern !== void 0 && !safeText3(secret.pattern, 64)) throw new Error(`${sat}.pattern must be a non-empty prefix string`);
|
|
1425
|
+
if (secret.required !== void 0 && typeof secret.required !== "boolean") throw new Error(`${sat}.required must be a boolean`);
|
|
1426
|
+
if (names.has(secret.name)) throw new Error(`${at} declares secret "${secret.name}" twice`);
|
|
1427
|
+
names.add(secret.name);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1295
1430
|
function validateSeeds(integration, at) {
|
|
1296
1431
|
if (integration.seeds === void 0) return;
|
|
1297
1432
|
if (!Array.isArray(integration.seeds)) throw new Error(`${at}.seeds must be an array`);
|
|
1298
1433
|
const ids = /* @__PURE__ */ new Set();
|
|
1299
1434
|
for (const [index, seed] of integration.seeds.entries()) {
|
|
1300
1435
|
const sat = `${at}.seeds[${index}]`;
|
|
1301
|
-
if (!
|
|
1436
|
+
if (!isRecord6(seed) || !safeText3(seed.id, 200) || !safeText3(seed.ns, 200)) throw new Error(`${sat} requires id and ns`);
|
|
1302
1437
|
if (ids.has(seed.id)) throw new Error(`${at} has duplicate seed id "${seed.id}"`);
|
|
1303
1438
|
ids.add(seed.id);
|
|
1304
|
-
if (!
|
|
1439
|
+
if (!isRecord6(seed.key) || !safeText3(seed.key.attr, 200) || !safeText3(seed.key.value, 2048)) {
|
|
1305
1440
|
throw new Error(`${sat}.key requires string attr and value`);
|
|
1306
1441
|
}
|
|
1307
|
-
if (!
|
|
1442
|
+
if (!isRecord6(seed.attrs)) throw new Error(`${sat}.attrs must be an object`);
|
|
1308
1443
|
if (Object.hasOwn(seed.attrs, seed.key.attr) && seed.attrs[seed.key.attr] !== seed.key.value) {
|
|
1309
1444
|
throw new Error(`${sat}.attrs.${seed.key.attr} conflicts with its natural key`);
|
|
1310
1445
|
}
|
|
@@ -1315,16 +1450,16 @@ function validateProbes(integration, at) {
|
|
|
1315
1450
|
if (!Array.isArray(integration.probes)) throw new Error(`${at}.probes must be an array`);
|
|
1316
1451
|
for (const [index, probe] of integration.probes.entries()) {
|
|
1317
1452
|
const pat = `${at}.probes[${index}]`;
|
|
1318
|
-
if (!
|
|
1453
|
+
if (!isRecord6(probe) || !safeProbePath(probe.path)) throw new Error(`${pat}.path must be an absolute path without query or fragment`);
|
|
1319
1454
|
if (!Number.isInteger(probe.expectedStatus) || probe.expectedStatus < 100 || probe.expectedStatus > 599) {
|
|
1320
1455
|
throw new Error(`${pat}.expectedStatus must be an HTTP status`);
|
|
1321
1456
|
}
|
|
1322
1457
|
}
|
|
1323
1458
|
}
|
|
1324
|
-
function
|
|
1459
|
+
function isRecord6(value2) {
|
|
1325
1460
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1326
1461
|
}
|
|
1327
|
-
function
|
|
1462
|
+
function safeText3(value2, max) {
|
|
1328
1463
|
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1329
1464
|
}
|
|
1330
1465
|
function safeProbePath(value2) {
|
|
@@ -1333,13 +1468,15 @@ function safeProbePath(value2) {
|
|
|
1333
1468
|
function validId(value2) {
|
|
1334
1469
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1335
1470
|
}
|
|
1336
|
-
function
|
|
1471
|
+
function unique2(values) {
|
|
1337
1472
|
return [...new Set(values.filter(Boolean))];
|
|
1338
1473
|
}
|
|
1474
|
+
var SECRET_NAME;
|
|
1339
1475
|
var init_integration_validation = __esm({
|
|
1340
1476
|
"src/integration-validation.ts"() {
|
|
1341
1477
|
"use strict";
|
|
1342
1478
|
init_cjs_shims();
|
|
1479
|
+
SECRET_NAME = /^\$?[a-z][a-z0-9_]*$/;
|
|
1343
1480
|
}
|
|
1344
1481
|
});
|
|
1345
1482
|
|
|
@@ -1354,10 +1491,10 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1354
1491
|
validateRawConfig(raw, resolved);
|
|
1355
1492
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1356
1493
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
1357
|
-
const envs =
|
|
1358
|
-
const services =
|
|
1494
|
+
const envs = unique3(raw.envs?.length ? raw.envs : DEFAULT_ENVS);
|
|
1495
|
+
const services = unique3(raw.services?.length ? raw.services : DEFAULT_SERVICES);
|
|
1359
1496
|
validateServices(services, resolved);
|
|
1360
|
-
validateCalendarConfig(raw,
|
|
1497
|
+
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1361
1498
|
const local = {
|
|
1362
1499
|
tokenFile: (0, import_node_path5.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1363
1500
|
credentialsFile: (0, import_node_path5.resolve)(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
@@ -1405,26 +1542,6 @@ function buildPlan(cfg) {
|
|
|
1405
1542
|
aiProvider: cfg.ai?.provider
|
|
1406
1543
|
};
|
|
1407
1544
|
}
|
|
1408
|
-
function calendarServiceConfig(cfg, env) {
|
|
1409
|
-
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
1410
|
-
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1411
|
-
const google = cfg.calendar?.google;
|
|
1412
|
-
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
1413
|
-
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1414
|
-
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1415
|
-
const availability = unique2(configured.map((id) => id.trim()));
|
|
1416
|
-
return {
|
|
1417
|
-
provider: "google",
|
|
1418
|
-
access: "book",
|
|
1419
|
-
bookingCalendarId: google.bookingCalendar?.[env]?.trim() ?? availability[0],
|
|
1420
|
-
availabilityCalendars: availability
|
|
1421
|
-
};
|
|
1422
|
-
}
|
|
1423
|
-
function calendarBookingPageUrl(cfg, env) {
|
|
1424
|
-
const value2 = cfg.calendar?.google.bookingPageUrl?.[env];
|
|
1425
|
-
if (value2 === void 0 || value2 === null) return value2;
|
|
1426
|
-
return new URL(value2).toString();
|
|
1427
|
-
}
|
|
1428
1545
|
function rulesFromSchema(schema) {
|
|
1429
1546
|
const entities = serializedEntities(schema);
|
|
1430
1547
|
return Object.fromEntries(
|
|
@@ -1456,69 +1573,9 @@ function validateRawConfig(raw, path) {
|
|
|
1456
1573
|
throw new Error(`${path}: services must be an array of non-empty names`);
|
|
1457
1574
|
}
|
|
1458
1575
|
validateAiConfig(cfg, path);
|
|
1576
|
+
validateSecrets(cfg.secrets, `${path}: config`);
|
|
1459
1577
|
validateIntegrations(cfg, path, DEFAULT_SERVICES);
|
|
1460
1578
|
}
|
|
1461
|
-
function validateCalendarConfig(cfg, envs, services, path) {
|
|
1462
|
-
const enabled = services.includes("calendar");
|
|
1463
|
-
if (!cfg.calendar) {
|
|
1464
|
-
if (enabled) throw new Error(`${path}: calendar.google is required when services includes "calendar"`);
|
|
1465
|
-
return;
|
|
1466
|
-
}
|
|
1467
|
-
if (!isRecord6(cfg.calendar)) throw new Error(`${path}: calendar must be an object`);
|
|
1468
|
-
assertOnly2(cfg.calendar, ["google"], `${path}: calendar`);
|
|
1469
|
-
if (!isRecord6(cfg.calendar.google)) throw new Error(`${path}: calendar.google must be an object`);
|
|
1470
|
-
const google = cfg.calendar.google;
|
|
1471
|
-
assertOnly2(
|
|
1472
|
-
google,
|
|
1473
|
-
["availabilityCalendars", "calendars", "bookingCalendar", "bookingPageUrl"],
|
|
1474
|
-
`${path}: calendar.google`
|
|
1475
|
-
);
|
|
1476
|
-
const availabilityKey = google.availabilityCalendars !== void 0 ? "availabilityCalendars" : google.calendars !== void 0 ? "calendars" : null;
|
|
1477
|
-
if (!availabilityKey || google.availabilityCalendars !== void 0 && google.calendars !== void 0) {
|
|
1478
|
-
throw new Error(`${path}: calendar.google requires exactly one of availabilityCalendars or calendars (legacy)`);
|
|
1479
|
-
}
|
|
1480
|
-
const availability = google[availabilityKey];
|
|
1481
|
-
if (!isRecord6(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1482
|
-
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1483
|
-
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1484
|
-
for (const env of envs) {
|
|
1485
|
-
const ids = availability[env];
|
|
1486
|
-
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1487
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
for (const [env, ids] of Object.entries(availability)) {
|
|
1491
|
-
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1492
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1493
|
-
}
|
|
1494
|
-
if (ids.length > 10) {
|
|
1495
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1496
|
-
}
|
|
1497
|
-
if (ids.some((id) => !safeText3(id, 1024))) {
|
|
1498
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
if (google.bookingCalendar !== void 0) {
|
|
1502
|
-
if (!isRecord6(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1503
|
-
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1504
|
-
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1505
|
-
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1506
|
-
if (!safeText3(value2, 1024)) {
|
|
1507
|
-
throw new Error(`${path}: calendar.google.bookingCalendar.${env} must be a calendar id`);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
if (google.bookingPageUrl !== void 0) {
|
|
1512
|
-
if (!isRecord6(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1513
|
-
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1514
|
-
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1515
|
-
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1516
|
-
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
1517
|
-
throw new Error(`${path}: calendar.google.bookingPageUrl.${env} must be an HTTPS URL without credentials or fragment`);
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
1579
|
function validateServices(services, path) {
|
|
1523
1580
|
for (const service of services) {
|
|
1524
1581
|
const definition = (0, import_apps.appServiceDefinition)(service);
|
|
@@ -1532,25 +1589,6 @@ function validateServices(services, path) {
|
|
|
1532
1589
|
}
|
|
1533
1590
|
}
|
|
1534
1591
|
}
|
|
1535
|
-
function assertOnly2(value2, allowed, label) {
|
|
1536
|
-
const extra = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
1537
|
-
if (extra) throw new Error(`${label}.${extra} is not supported`);
|
|
1538
|
-
}
|
|
1539
|
-
function isRecord6(value2) {
|
|
1540
|
-
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1541
|
-
}
|
|
1542
|
-
function safeText3(value2, max) {
|
|
1543
|
-
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1544
|
-
}
|
|
1545
|
-
function safeHttpsUrl(value2) {
|
|
1546
|
-
if (typeof value2 !== "string" || value2.length > 2048) return false;
|
|
1547
|
-
try {
|
|
1548
|
-
const url = new URL(value2);
|
|
1549
|
-
return url.protocol === "https:" && !url.username && !url.password && !url.hash;
|
|
1550
|
-
} catch {
|
|
1551
|
-
return false;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
1592
|
function validId2(value2) {
|
|
1555
1593
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1556
1594
|
}
|
|
@@ -1565,7 +1603,7 @@ async function loadConfigModule(path) {
|
|
|
1565
1603
|
function trimSlash(value2) {
|
|
1566
1604
|
return value2.replace(/\/+$/, "");
|
|
1567
1605
|
}
|
|
1568
|
-
function
|
|
1606
|
+
function unique3(values) {
|
|
1569
1607
|
return [...new Set(values.filter(Boolean))];
|
|
1570
1608
|
}
|
|
1571
1609
|
var import_node_fs7, import_node_path5, import_node_url, import_apps, DEFAULT_PLATFORM, DEFAULT_ENVS, DEFAULT_SERVICES, configImportSerial, GOOGLE_CALENDAR_EVENTS_SCOPE;
|
|
@@ -1578,7 +1616,9 @@ var init_config = __esm({
|
|
|
1578
1616
|
import_node_url = require("url");
|
|
1579
1617
|
import_apps = require("@odla-ai/apps");
|
|
1580
1618
|
init_ai_config_validation();
|
|
1619
|
+
init_calendar_config();
|
|
1581
1620
|
init_integration_validation();
|
|
1621
|
+
init_calendar_config();
|
|
1582
1622
|
DEFAULT_PLATFORM = "https://odla.ai";
|
|
1583
1623
|
DEFAULT_ENVS = ["dev"];
|
|
1584
1624
|
DEFAULT_SERVICES = ["db", "ai"];
|
|
@@ -4661,6 +4701,74 @@ var init_integrations = __esm({
|
|
|
4661
4701
|
}
|
|
4662
4702
|
});
|
|
4663
4703
|
|
|
4704
|
+
// src/secret-contract.ts
|
|
4705
|
+
function resolveSecretContract(cfg) {
|
|
4706
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4707
|
+
const declarations = [
|
|
4708
|
+
...(cfg.secrets ?? []).map((secret) => ({ source: APP_SOURCE, secret })),
|
|
4709
|
+
...(cfg.integrations ?? []).flatMap(
|
|
4710
|
+
(integration) => (integration.secrets ?? []).map((secret) => ({ source: integration.id, secret }))
|
|
4711
|
+
)
|
|
4712
|
+
];
|
|
4713
|
+
for (const { source, secret } of declarations) {
|
|
4714
|
+
const existing = byName.get(secret.name);
|
|
4715
|
+
if (!existing) {
|
|
4716
|
+
byName.set(secret.name, { ...secret, required: secret.required !== false, sources: [source] });
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
existing.sources.push(source);
|
|
4720
|
+
existing.required = existing.required || secret.required !== false;
|
|
4721
|
+
existing.pattern ??= secret.pattern;
|
|
4722
|
+
}
|
|
4723
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
4724
|
+
}
|
|
4725
|
+
function secretContractWarnings(contract, cfg) {
|
|
4726
|
+
const warnings = [];
|
|
4727
|
+
const declaredPatterns = /* @__PURE__ */ new Map();
|
|
4728
|
+
for (const integration of cfg.integrations ?? []) {
|
|
4729
|
+
for (const secret of integration.secrets ?? []) {
|
|
4730
|
+
if (!secret.pattern) continue;
|
|
4731
|
+
const seen = declaredPatterns.get(secret.name) ?? /* @__PURE__ */ new Map();
|
|
4732
|
+
seen.set(integration.id, secret.pattern);
|
|
4733
|
+
declaredPatterns.set(secret.name, seen);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
for (const secret of cfg.secrets ?? []) {
|
|
4737
|
+
if (!secret.pattern) continue;
|
|
4738
|
+
const seen = declaredPatterns.get(secret.name) ?? /* @__PURE__ */ new Map();
|
|
4739
|
+
seen.set(APP_SOURCE, secret.pattern);
|
|
4740
|
+
declaredPatterns.set(secret.name, seen);
|
|
4741
|
+
}
|
|
4742
|
+
for (const [name, seen] of declaredPatterns) {
|
|
4743
|
+
const distinct = [...new Set(seen.values())];
|
|
4744
|
+
if (distinct.length > 1) {
|
|
4745
|
+
const detail = [...seen].map(([source, pattern]) => `${source} expects "${pattern}"`).join(", ");
|
|
4746
|
+
warnings.push(`secret "${name}" has conflicting patterns \u2014 ${detail}; one of them will reject a valid value`);
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
if (contract.length > 0 && !cfg.services.includes("db")) {
|
|
4750
|
+
const names = contract.map((secret) => secret.name).join(", ");
|
|
4751
|
+
warnings.push(`secrets are declared (${names}) but the db service is off \u2014 nothing can read the tenant vault`);
|
|
4752
|
+
}
|
|
4753
|
+
return warnings;
|
|
4754
|
+
}
|
|
4755
|
+
function formatSecretContract(contract) {
|
|
4756
|
+
return contract.map((secret) => {
|
|
4757
|
+
const flags = [secret.required ? "required" : "optional"];
|
|
4758
|
+
if (secret.reserved) flags.push("reserved");
|
|
4759
|
+
if (secret.pattern) flags.push(`${secret.pattern}\u2026`);
|
|
4760
|
+
return ` ${secret.name} (${flags.join(", ")}) \u2014 ${secret.sources.join(", ")}`;
|
|
4761
|
+
});
|
|
4762
|
+
}
|
|
4763
|
+
var APP_SOURCE;
|
|
4764
|
+
var init_secret_contract = __esm({
|
|
4765
|
+
"src/secret-contract.ts"() {
|
|
4766
|
+
"use strict";
|
|
4767
|
+
init_cjs_shims();
|
|
4768
|
+
APP_SOURCE = "app";
|
|
4769
|
+
}
|
|
4770
|
+
});
|
|
4771
|
+
|
|
4664
4772
|
// src/doctor.ts
|
|
4665
4773
|
async function doctor(options) {
|
|
4666
4774
|
const out = options.stdout ?? console;
|
|
@@ -4677,6 +4785,9 @@ async function doctor(options) {
|
|
|
4677
4785
|
out.log(`schema: ${schema ? `${entities.length} entities` : "none"}`);
|
|
4678
4786
|
out.log(`rules: ${rules ? `${Object.keys(rules).length} namespaces` : "none"}`);
|
|
4679
4787
|
out.log(`ai: ${cfg.services.includes("ai") ? cfg.ai?.provider ? `byok/${cfg.ai.provider}` : "hosted" : "not enabled"}`);
|
|
4788
|
+
const contract = resolveSecretContract(cfg);
|
|
4789
|
+
out.log(`secrets: ${contract.length ? `${contract.length} declared` : "none declared"}`);
|
|
4790
|
+
for (const line of formatSecretContract(contract)) out.log(line);
|
|
4680
4791
|
if (cfg.services.includes("calendar")) {
|
|
4681
4792
|
const calendar = cfg.envs.map((env) => {
|
|
4682
4793
|
const resolved = calendarServiceConfig(cfg, env);
|
|
@@ -4697,6 +4808,7 @@ async function doctor(options) {
|
|
|
4697
4808
|
}
|
|
4698
4809
|
}
|
|
4699
4810
|
warnings.push(...integrationWarnings(database.integrations, schema, rules));
|
|
4811
|
+
warnings.push(...secretContractWarnings(contract, cfg));
|
|
4700
4812
|
if (cfg.services.includes("ai") && cfg.ai?.mode === "byok" && !cfg.ai.provider) {
|
|
4701
4813
|
warnings.push("ai.mode is byok but ai.provider is not set");
|
|
4702
4814
|
}
|
|
@@ -4745,6 +4857,7 @@ var init_doctor = __esm({
|
|
|
4745
4857
|
init_doctor_runbooks();
|
|
4746
4858
|
init_integrations();
|
|
4747
4859
|
init_local();
|
|
4860
|
+
init_secret_contract();
|
|
4748
4861
|
}
|
|
4749
4862
|
});
|
|
4750
4863
|
|
|
@@ -5072,6 +5185,81 @@ var init_secrets_set = __esm({
|
|
|
5072
5185
|
}
|
|
5073
5186
|
});
|
|
5074
5187
|
|
|
5188
|
+
// src/secrets-status.ts
|
|
5189
|
+
async function secretsStatus(options) {
|
|
5190
|
+
const out = options.stdout ?? console;
|
|
5191
|
+
const doFetch = options.fetch ?? fetch;
|
|
5192
|
+
const cfg = await loadProjectConfig(options.configPath);
|
|
5193
|
+
if (!cfg.envs.includes(options.env)) {
|
|
5194
|
+
throw new Error(`env "${options.env}" is not in config envs (${cfg.envs.join(", ")})`);
|
|
5195
|
+
}
|
|
5196
|
+
const tenantId = (0, import_apps11.tenantIdFor)(cfg.app.id, options.env);
|
|
5197
|
+
const contract = resolveSecretContract(cfg);
|
|
5198
|
+
const token = await getDeveloperToken(cfg, options, doFetch, out);
|
|
5199
|
+
const res = await doFetch(`${cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/secrets`, {
|
|
5200
|
+
headers: { authorization: `Bearer ${token}` }
|
|
5201
|
+
});
|
|
5202
|
+
if (!res.ok) {
|
|
5203
|
+
const detail = (await res.text().catch(() => "")).slice(0, 300);
|
|
5204
|
+
throw new Error(`list secrets for ${tenantId} failed (${res.status}): ${detail || "request failed"}`);
|
|
5205
|
+
}
|
|
5206
|
+
const body = await res.json();
|
|
5207
|
+
const stored = new Set((body.secrets ?? []).map((entry) => String(entry.name)));
|
|
5208
|
+
const report4 = buildReport(cfg.app.id, options.env, tenantId, contract, stored);
|
|
5209
|
+
if (options.json) out.log(JSON.stringify(report4, null, 2));
|
|
5210
|
+
else printReport(report4, out);
|
|
5211
|
+
return report4;
|
|
5212
|
+
}
|
|
5213
|
+
function buildReport(appId, env, tenant, contract, stored) {
|
|
5214
|
+
const declared = new Set(contract.map((secret) => secret.name));
|
|
5215
|
+
const rows = contract.map((secret) => ({
|
|
5216
|
+
name: secret.name,
|
|
5217
|
+
state: secret.reserved ? "reserved" : stored.has(secret.name) ? "set" : "missing",
|
|
5218
|
+
required: secret.required,
|
|
5219
|
+
sources: secret.sources,
|
|
5220
|
+
description: secret.description
|
|
5221
|
+
}));
|
|
5222
|
+
for (const name of [...stored].sort()) {
|
|
5223
|
+
if (!declared.has(name)) rows.push({ name, state: "undeclared", required: false, sources: [] });
|
|
5224
|
+
}
|
|
5225
|
+
const ok = rows.every((row) => row.state !== "missing" || !row.required);
|
|
5226
|
+
return { app: appId, env, tenant, secrets: rows, ok };
|
|
5227
|
+
}
|
|
5228
|
+
function printReport(report4, out) {
|
|
5229
|
+
out.log(`${report4.app} (${report4.tenant})`);
|
|
5230
|
+
if (report4.secrets.length === 0) {
|
|
5231
|
+
out.log(" no secrets declared and none stored");
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
for (const row of report4.secrets) {
|
|
5235
|
+
const label = row.state === "missing" && !row.required ? "missing (optional)" : row.state;
|
|
5236
|
+
const suffix = row.sources.length ? ` \u2014 ${row.sources.join(", ")}` : "";
|
|
5237
|
+
out.log(` ${label.padEnd(18)} ${row.name}${suffix}`);
|
|
5238
|
+
}
|
|
5239
|
+
const missing = report4.secrets.filter((row) => row.state === "missing" && row.required);
|
|
5240
|
+
if (missing.length) {
|
|
5241
|
+
out.log("");
|
|
5242
|
+
for (const row of missing) {
|
|
5243
|
+
out.log(`${row.name} is required but not set \u2014 "odla-ai secrets set ${row.name} --env ${report4.env} --stdin"`);
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
if (report4.secrets.some((row) => row.state === "reserved")) {
|
|
5247
|
+
out.log("");
|
|
5248
|
+
out.log('"reserved" slots are never enumerated by the vault; presence cannot be confirmed here.');
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
var import_apps11;
|
|
5252
|
+
var init_secrets_status = __esm({
|
|
5253
|
+
"src/secrets-status.ts"() {
|
|
5254
|
+
"use strict";
|
|
5255
|
+
init_cjs_shims();
|
|
5256
|
+
import_apps11 = require("@odla-ai/apps");
|
|
5257
|
+
init_config();
|
|
5258
|
+
init_secret_contract();
|
|
5259
|
+
init_token();
|
|
5260
|
+
}
|
|
5261
|
+
});
|
|
5262
|
+
|
|
5075
5263
|
// src/skill-adapters.ts
|
|
5076
5264
|
function claudeAdapter(skill, canonical) {
|
|
5077
5265
|
const match = canonical.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -5566,9 +5754,22 @@ async function secretsCommand(parsed, deps) {
|
|
|
5566
5754
|
await (sub === "set" ? secretsSet(options) : secretsSetClerkKey(options));
|
|
5567
5755
|
return;
|
|
5568
5756
|
}
|
|
5757
|
+
if (sub === "status") {
|
|
5758
|
+
assertArgs(parsed, ["config", "env", "token", "email", "json"], 2);
|
|
5759
|
+
await secretsStatus({
|
|
5760
|
+
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
5761
|
+
env: requiredString(parsed.options.env, "--env"),
|
|
5762
|
+
json: parsed.options.json === true,
|
|
5763
|
+
token: stringOpt(parsed.options.token),
|
|
5764
|
+
email: stringOpt(parsed.options.email),
|
|
5765
|
+
fetch: deps.fetch,
|
|
5766
|
+
stdout: deps.stdout
|
|
5767
|
+
});
|
|
5768
|
+
return;
|
|
5769
|
+
}
|
|
5569
5770
|
if (sub !== "push") {
|
|
5570
5771
|
throw new Error(
|
|
5571
|
-
`unknown secrets subcommand "${sub ?? ""}". Try "odla-ai secrets push --env dev", "odla-ai secrets set <name> --env dev --stdin", or "odla-ai secrets set-clerk-key --env dev --stdin".`
|
|
5772
|
+
`unknown secrets subcommand "${sub ?? ""}". Try "odla-ai secrets push --env dev", "odla-ai secrets status --env dev", "odla-ai secrets set <name> --env dev --stdin", or "odla-ai secrets set-clerk-key --env dev --stdin".`
|
|
5572
5773
|
);
|
|
5573
5774
|
}
|
|
5574
5775
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
@@ -5725,6 +5926,7 @@ var init_cli_project = __esm({
|
|
|
5725
5926
|
init_init();
|
|
5726
5927
|
init_secrets();
|
|
5727
5928
|
init_secrets_set();
|
|
5929
|
+
init_secrets_status();
|
|
5728
5930
|
init_skill();
|
|
5729
5931
|
init_smoke();
|
|
5730
5932
|
SKILL_OPTS = ["dir", "global", "force", "agent", "harness"];
|
|
@@ -6183,9 +6385,9 @@ function dependenciesOf(values, influence = "data") {
|
|
|
6183
6385
|
result.push({ ref, influence, promptSafetyAtUse: value2.label.promptSafety });
|
|
6184
6386
|
}
|
|
6185
6387
|
}
|
|
6186
|
-
const
|
|
6187
|
-
for (const dep of result)
|
|
6188
|
-
return [...
|
|
6388
|
+
const unique4 = /* @__PURE__ */ new Map();
|
|
6389
|
+
for (const dep of result) unique4.set(`${dep.ref.kind}\0${dep.ref.id}\0${dep.influence}\0${dep.promptSafetyAtUse}`, dep);
|
|
6390
|
+
return [...unique4.values()];
|
|
6189
6391
|
}
|
|
6190
6392
|
var init_chunk_L5DYU2E2 = __esm({
|
|
6191
6393
|
"../camel/dist/chunk-L5DYU2E2.js"() {
|
|
@@ -7089,7 +7291,7 @@ var init_code2 = __esm({
|
|
|
7089
7291
|
}
|
|
7090
7292
|
});
|
|
7091
7293
|
|
|
7092
|
-
// ../harness/dist/chunk-
|
|
7294
|
+
// ../harness/dist/chunk-ANNX7VGK.js
|
|
7093
7295
|
async function digestStagedWorkspace(root, limits) {
|
|
7094
7296
|
const files = [];
|
|
7095
7297
|
const walk = async (directory) => {
|
|
@@ -7234,6 +7436,16 @@ function createCodeRuntimeControlClient(options) {
|
|
|
7234
7436
|
}
|
|
7235
7437
|
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
7236
7438
|
},
|
|
7439
|
+
recallMemories: async (sessionId, subjects, limit) => {
|
|
7440
|
+
const response2 = await call2(
|
|
7441
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
7442
|
+
{ subjects: [...subjects], limit }
|
|
7443
|
+
);
|
|
7444
|
+
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
7445
|
+
},
|
|
7446
|
+
rememberMemory: async (sessionId, memory) => {
|
|
7447
|
+
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
7448
|
+
},
|
|
7237
7449
|
reportSessionFailure: async (sessionId, message2) => {
|
|
7238
7450
|
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
7239
7451
|
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
@@ -8774,6 +8986,28 @@ function validateOptions(options) {
|
|
|
8774
8986
|
throw new TypeError("Code tool broker read-only prefix is invalid");
|
|
8775
8987
|
}
|
|
8776
8988
|
}
|
|
8989
|
+
function validateMemory(memory) {
|
|
8990
|
+
if (!memory.subject.includes(":")) {
|
|
8991
|
+
throw new TypeError(`memory subject must be a graph node id, got "${memory.subject}"`);
|
|
8992
|
+
}
|
|
8993
|
+
const body = memory.body.trim();
|
|
8994
|
+
if (!body) throw new TypeError("a memory needs a body");
|
|
8995
|
+
if (body.length > MAX_MEMORY_BODY) throw new TypeError("memory body exceeds its bound");
|
|
8996
|
+
if (!memory.authorId.trim()) throw new TypeError("a memory needs an author");
|
|
8997
|
+
}
|
|
8998
|
+
function hazardFromAttempt(input) {
|
|
8999
|
+
const body = [
|
|
9000
|
+
`Attempt ${input.attempt} at "${input.goal.slice(0, 200)}" failed its proof.`,
|
|
9001
|
+
input.feedback.replace(/\s+/g, " ").slice(0, MAX_MEMORY_BODY - 300)
|
|
9002
|
+
].join(" ");
|
|
9003
|
+
return input.touched.slice(0, 10).map((path) => ({
|
|
9004
|
+
subject: path.includes(":") ? path : `file:${path}`,
|
|
9005
|
+
kind: "hazard",
|
|
9006
|
+
body,
|
|
9007
|
+
evidence: { kind: "gate", ref: input.verificationId },
|
|
9008
|
+
authorId: input.authorId
|
|
9009
|
+
}));
|
|
9010
|
+
}
|
|
8777
9011
|
async function runGoal(spec, attempt) {
|
|
8778
9012
|
assertBudget(spec.budget);
|
|
8779
9013
|
const now = spec.now ?? Date.now;
|
|
@@ -8964,6 +9198,9 @@ function pursueRuntimeGoal(input) {
|
|
|
8964
9198
|
return { gatePassed: false, feedback: "", tokens: outcome.tokens, error: outcome.error };
|
|
8965
9199
|
}
|
|
8966
9200
|
const verdict = await input.gate(attempt);
|
|
9201
|
+
if (!verdict.passed && input.memory) {
|
|
9202
|
+
await rememberFailure(input, attempt, verdict.feedback);
|
|
9203
|
+
}
|
|
8967
9204
|
return {
|
|
8968
9205
|
gatePassed: verdict.passed,
|
|
8969
9206
|
feedback: verdict.feedback,
|
|
@@ -8974,6 +9211,25 @@ function pursueRuntimeGoal(input) {
|
|
|
8974
9211
|
}
|
|
8975
9212
|
);
|
|
8976
9213
|
}
|
|
9214
|
+
async function rememberFailure(input, attempt, feedback) {
|
|
9215
|
+
if (!input.memory || !feedback.trim()) return;
|
|
9216
|
+
try {
|
|
9217
|
+
const touched = await input.touched?.(attempt) ?? [];
|
|
9218
|
+
if (touched.length === 0) return;
|
|
9219
|
+
for (const memory of hazardFromAttempt({
|
|
9220
|
+
goal: input.spec.goal,
|
|
9221
|
+
attempt,
|
|
9222
|
+
feedback,
|
|
9223
|
+
touched,
|
|
9224
|
+
verificationId: `goal-${attempt}`,
|
|
9225
|
+
authorId: input.memory.authorId
|
|
9226
|
+
})) {
|
|
9227
|
+
validateMemory(memory);
|
|
9228
|
+
await input.memory.store.remember(memory);
|
|
9229
|
+
}
|
|
9230
|
+
} catch {
|
|
9231
|
+
}
|
|
9232
|
+
}
|
|
8977
9233
|
function goalEventLine(event) {
|
|
8978
9234
|
if (event.type === "attempt_started") return `Goal attempt ${event.attempt} starting.`;
|
|
8979
9235
|
if (event.type === "attempt_failed") return `Attempt ${event.attempt} did not satisfy the proof.`;
|
|
@@ -9019,9 +9275,9 @@ async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
|
9019
9275
|
const bounded = event.type === "message" ? { ...event, body: event.body.trim().slice(0, 2e4) || `${event.actor} event` } : event;
|
|
9020
9276
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
9021
9277
|
}
|
|
9022
|
-
var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record4, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
|
|
9023
|
-
var
|
|
9024
|
-
"../harness/dist/chunk-
|
|
9278
|
+
var import_crypto, import_promises5, import_path5, import_child_process4, import_promises6, import_path6, import_child_process5, import_process2, import_crypto2, import_crypto3, import_fs2, import_promises7, import_path7, import_promises8, import_os3, import_path8, import_ai4, import_promises9, import_path9, import_promises10, import_promises11, import_path10, import_crypto4, CODE_RUNTIME_PROTOCOL_VERSION, CodeRuntimeReconciler, CodeRuntimeControlError, record4, invalid, RESERVED, SECRET, PATH, FORBIDDEN, ARTIFACT_PATH, PRIVATE_ARTIFACT_PART, SHA3, DIGEST3, ID3, RULE, DEFAULT_PREFIXES, DEFAULT_SUFFIXES, message, CodeRuntimeCheckpointManager, record22, SOURCE_LIMITS, RESERVED2, SECRET2, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, SYSTEM_PROMPT_FOR, DEFAULT_MAX_FILES, DEFAULT_MAX_RESULTS, DEFAULT_MAX_FILE_BYTES, DESTINATIONS, READ, LIST, SEARCH, GRAPH, PATCH, RECIPE, cache, shortId, GRAPH_TOOLS, MAX_MEMORY_BODY, POSITIVE, digestRuntimeValue, runtimeErrorMessage, CodePiRuntimeEngine;
|
|
9279
|
+
var init_chunk_ANNX7VGK = __esm({
|
|
9280
|
+
"../harness/dist/chunk-ANNX7VGK.js"() {
|
|
9025
9281
|
"use strict";
|
|
9026
9282
|
init_cjs_shims();
|
|
9027
9283
|
init_chunk_GKDKIU4P();
|
|
@@ -9251,6 +9507,7 @@ Never claim a build or test passed unless odla_run_recipe returned that result.`
|
|
|
9251
9507
|
"sandbox.who_imports",
|
|
9252
9508
|
"sandbox.who_touches"
|
|
9253
9509
|
]);
|
|
9510
|
+
MAX_MEMORY_BODY = 4e3;
|
|
9254
9511
|
POSITIVE = (value2) => Number.isFinite(value2) && Number(value2) > 0 ? Number(value2) : void 0;
|
|
9255
9512
|
digestRuntimeValue = (value2) => `sha256:${(0, import_crypto4.createHash)("sha256").update(value2).digest("hex")}`;
|
|
9256
9513
|
runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
|
|
@@ -9535,7 +9792,7 @@ var init_node = __esm({
|
|
|
9535
9792
|
"../harness/dist/node.js"() {
|
|
9536
9793
|
"use strict";
|
|
9537
9794
|
init_cjs_shims();
|
|
9538
|
-
|
|
9795
|
+
init_chunk_ANNX7VGK();
|
|
9539
9796
|
init_chunk_GKDKIU4P();
|
|
9540
9797
|
MEASURED_PREMIUM = Object.freeze({
|
|
9541
9798
|
/** 3 racers vs pure depth at equal budget: 21,044 / 7,936. */
|
|
@@ -10504,7 +10761,9 @@ Commands:
|
|
|
10504
10761
|
copilot, gemini, or agents (repeatable or comma-separated).
|
|
10505
10762
|
secrets Push configured db/o11y secrets into the Worker via wrangler
|
|
10506
10763
|
stdin; set stores a tenant-vault secret and set-clerk-key the
|
|
10507
|
-
reserved Clerk secret key, write-only from stdin or an env var
|
|
10764
|
+
reserved Clerk secret key, write-only from stdin or an env var;
|
|
10765
|
+
status compares the secrets the config declares against the
|
|
10766
|
+
names the environment's vault holds (--json for a report).
|
|
10508
10767
|
version Print the CLI version.
|
|
10509
10768
|
|
|
10510
10769
|
Safety:
|
|
@@ -12738,7 +12997,7 @@ var init_integration_provision = __esm({
|
|
|
12738
12997
|
|
|
12739
12998
|
// src/provision-credentials.ts
|
|
12740
12999
|
async function provisionEnvCredentials(opts) {
|
|
12741
|
-
const tenantId = (0,
|
|
13000
|
+
const tenantId = (0, import_apps12.tenantIdFor)(opts.cfg.app.id, opts.env);
|
|
12742
13001
|
const prior = opts.credentials?.envs[opts.env];
|
|
12743
13002
|
let credentials = opts.credentials;
|
|
12744
13003
|
let dbKey = opts.cfg.services.includes("db") && !opts.rotateDb ? prior?.dbKey : void 0;
|
|
@@ -12829,12 +13088,12 @@ async function safeText7(res) {
|
|
|
12829
13088
|
return "";
|
|
12830
13089
|
}
|
|
12831
13090
|
}
|
|
12832
|
-
var
|
|
13091
|
+
var import_apps12;
|
|
12833
13092
|
var init_provision_credentials = __esm({
|
|
12834
13093
|
"src/provision-credentials.ts"() {
|
|
12835
13094
|
"use strict";
|
|
12836
13095
|
init_cjs_shims();
|
|
12837
|
-
|
|
13096
|
+
import_apps12 = require("@odla-ai/apps");
|
|
12838
13097
|
init_local();
|
|
12839
13098
|
init_redact();
|
|
12840
13099
|
}
|
|
@@ -13036,7 +13295,7 @@ async function provision(options) {
|
|
|
13036
13295
|
optionalProjectCapabilities: ["app.manage"],
|
|
13037
13296
|
forceReview: options.requestGrant
|
|
13038
13297
|
});
|
|
13039
|
-
const apps = (0,
|
|
13298
|
+
const apps = (0, import_apps13.createAppsClient)({ endpoint: cfg.platformUrl, token, fetcher: { fetch: doFetch } });
|
|
13040
13299
|
const existing = await apps.resolveApp(cfg.app.id);
|
|
13041
13300
|
if (existing) {
|
|
13042
13301
|
out.log(`app: ${cfg.app.id} already exists`);
|
|
@@ -13048,7 +13307,7 @@ async function provision(options) {
|
|
|
13048
13307
|
try {
|
|
13049
13308
|
await apps.createApp({ name: cfg.app.name, appId: cfg.app.id });
|
|
13050
13309
|
} catch (error) {
|
|
13051
|
-
if (error instanceof
|
|
13310
|
+
if (error instanceof import_apps13.AppsError && error.status === 403) {
|
|
13052
13311
|
throw new Error(
|
|
13053
13312
|
`app "${cfg.app.id}" does not exist, and this authenticated agent credential has no owner-reviewed app.manage bootstrap grant for that exact id. Run "odla-ai provision --request-grant --email <odla-account>" to open the review URL and continue; developer ownership alone is not agent authority`,
|
|
13054
13313
|
{ cause: error }
|
|
@@ -13061,7 +13320,7 @@ async function provision(options) {
|
|
|
13061
13320
|
for (const env of cfg.envs) {
|
|
13062
13321
|
await assertTenantAdminAccess(doFetch, cfg, env, token);
|
|
13063
13322
|
}
|
|
13064
|
-
const serviceOrder = (0,
|
|
13323
|
+
const serviceOrder = (0, import_apps13.orderAppServices)(cfg.services);
|
|
13065
13324
|
for (const env of cfg.envs) {
|
|
13066
13325
|
for (const service of serviceOrder) {
|
|
13067
13326
|
if (service === "ai") {
|
|
@@ -13095,7 +13354,7 @@ async function provision(options) {
|
|
|
13095
13354
|
}
|
|
13096
13355
|
let devVarsCredentials = credentials;
|
|
13097
13356
|
for (const env of cfg.envs) {
|
|
13098
|
-
const tenantId = (0,
|
|
13357
|
+
const tenantId = (0, import_apps13.tenantIdFor)(cfg.app.id, env);
|
|
13099
13358
|
let dbKey;
|
|
13100
13359
|
if (options.pushSecrets) {
|
|
13101
13360
|
const delivered = await deliverRuntimeCredentials(cfg, {
|
|
@@ -13181,12 +13440,12 @@ async function provision(options) {
|
|
|
13181
13440
|
}
|
|
13182
13441
|
}
|
|
13183
13442
|
}
|
|
13184
|
-
var
|
|
13443
|
+
var import_apps13, import_ai5, import_node_process12;
|
|
13185
13444
|
var init_provision = __esm({
|
|
13186
13445
|
"src/provision.ts"() {
|
|
13187
13446
|
"use strict";
|
|
13188
13447
|
init_cjs_shims();
|
|
13189
|
-
|
|
13448
|
+
import_apps13 = require("@odla-ai/apps");
|
|
13190
13449
|
import_ai5 = require("@odla-ai/ai");
|
|
13191
13450
|
import_node_process12 = __toESM(require("process"), 1);
|
|
13192
13451
|
init_config();
|
|
@@ -13357,7 +13616,7 @@ var init_surface = __esm({
|
|
|
13357
13616
|
rm: {},
|
|
13358
13617
|
lint: {}
|
|
13359
13618
|
},
|
|
13360
|
-
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
13619
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
13361
13620
|
security: {
|
|
13362
13621
|
plan: {},
|
|
13363
13622
|
sources: {},
|