@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
|
@@ -990,6 +990,111 @@ function safeText(value2, max) {
|
|
|
990
990
|
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
991
991
|
}
|
|
992
992
|
|
|
993
|
+
// src/calendar-config.ts
|
|
994
|
+
function calendarServiceConfig(cfg, env) {
|
|
995
|
+
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
996
|
+
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
997
|
+
const google = cfg.calendar?.google;
|
|
998
|
+
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
999
|
+
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1000
|
+
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1001
|
+
const availability = unique(configured.map((id) => id.trim()));
|
|
1002
|
+
return {
|
|
1003
|
+
provider: "google",
|
|
1004
|
+
access: "book",
|
|
1005
|
+
bookingCalendarId: google.bookingCalendar?.[env]?.trim() ?? availability[0],
|
|
1006
|
+
availabilityCalendars: availability
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
function calendarBookingPageUrl(cfg, env) {
|
|
1010
|
+
const value2 = cfg.calendar?.google.bookingPageUrl?.[env];
|
|
1011
|
+
if (value2 === void 0 || value2 === null) return value2;
|
|
1012
|
+
return new URL(value2).toString();
|
|
1013
|
+
}
|
|
1014
|
+
function validateCalendarConfig(cfg, envs, services, path) {
|
|
1015
|
+
const enabled = services.includes("calendar");
|
|
1016
|
+
if (!cfg.calendar) {
|
|
1017
|
+
if (enabled) throw new Error(`${path}: calendar.google is required when services includes "calendar"`);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (!isRecord5(cfg.calendar)) throw new Error(`${path}: calendar must be an object`);
|
|
1021
|
+
assertOnly2(cfg.calendar, ["google"], `${path}: calendar`);
|
|
1022
|
+
if (!isRecord5(cfg.calendar.google)) throw new Error(`${path}: calendar.google must be an object`);
|
|
1023
|
+
const google = cfg.calendar.google;
|
|
1024
|
+
assertOnly2(
|
|
1025
|
+
google,
|
|
1026
|
+
["availabilityCalendars", "calendars", "bookingCalendar", "bookingPageUrl"],
|
|
1027
|
+
`${path}: calendar.google`
|
|
1028
|
+
);
|
|
1029
|
+
const availabilityKey = google.availabilityCalendars !== void 0 ? "availabilityCalendars" : google.calendars !== void 0 ? "calendars" : null;
|
|
1030
|
+
if (!availabilityKey || google.availabilityCalendars !== void 0 && google.calendars !== void 0) {
|
|
1031
|
+
throw new Error(`${path}: calendar.google requires exactly one of availabilityCalendars or calendars (legacy)`);
|
|
1032
|
+
}
|
|
1033
|
+
const availability = google[availabilityKey];
|
|
1034
|
+
if (!isRecord5(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1035
|
+
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1036
|
+
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1037
|
+
for (const env of envs) {
|
|
1038
|
+
const ids = availability[env];
|
|
1039
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1040
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
for (const [env, ids] of Object.entries(availability)) {
|
|
1044
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1045
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1046
|
+
}
|
|
1047
|
+
if (ids.length > 10) {
|
|
1048
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1049
|
+
}
|
|
1050
|
+
if (ids.some((id) => !safeText2(id, 1024))) {
|
|
1051
|
+
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (google.bookingCalendar !== void 0) {
|
|
1055
|
+
if (!isRecord5(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1056
|
+
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1057
|
+
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1058
|
+
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1059
|
+
if (!safeText2(value2, 1024)) {
|
|
1060
|
+
throw new Error(`${path}: calendar.google.bookingCalendar.${env} must be a calendar id`);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (google.bookingPageUrl !== void 0) {
|
|
1065
|
+
if (!isRecord5(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1066
|
+
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1067
|
+
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1068
|
+
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1069
|
+
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
1070
|
+
throw new Error(`${path}: calendar.google.bookingPageUrl.${env} must be an HTTPS URL without credentials or fragment`);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function assertOnly2(value2, allowed, label) {
|
|
1076
|
+
const extra = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
1077
|
+
if (extra) throw new Error(`${label}.${extra} is not supported`);
|
|
1078
|
+
}
|
|
1079
|
+
function isRecord5(value2) {
|
|
1080
|
+
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1081
|
+
}
|
|
1082
|
+
function safeText2(value2, max) {
|
|
1083
|
+
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1084
|
+
}
|
|
1085
|
+
function safeHttpsUrl(value2) {
|
|
1086
|
+
if (typeof value2 !== "string" || value2.length > 2048) return false;
|
|
1087
|
+
try {
|
|
1088
|
+
const url = new URL(value2);
|
|
1089
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.hash;
|
|
1090
|
+
} catch {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function unique(values) {
|
|
1095
|
+
return [...new Set(values.filter(Boolean))];
|
|
1096
|
+
}
|
|
1097
|
+
|
|
993
1098
|
// src/integration-validation.ts
|
|
994
1099
|
function validateIntegrations(cfg, path, defaultServices) {
|
|
995
1100
|
if (cfg.integrations === void 0) return;
|
|
@@ -997,36 +1102,61 @@ function validateIntegrations(cfg, path, defaultServices) {
|
|
|
997
1102
|
const ids = /* @__PURE__ */ new Set();
|
|
998
1103
|
for (const [index, integration] of cfg.integrations.entries()) {
|
|
999
1104
|
const at = `${path}: integrations[${index}]`;
|
|
1000
|
-
if (!
|
|
1105
|
+
if (!isRecord6(integration)) throw new Error(`${at} must be an object`);
|
|
1001
1106
|
if (!validId(integration.id)) throw new Error(`${at}.id must be lowercase letters, numbers, and hyphens`);
|
|
1002
1107
|
if (ids.has(integration.id)) throw new Error(`${path}: duplicate integration id "${integration.id}"`);
|
|
1003
1108
|
ids.add(integration.id);
|
|
1004
|
-
if (!
|
|
1005
|
-
if (!
|
|
1006
|
-
if (integration.schema !== void 0 && (!
|
|
1109
|
+
if (!safeText3(integration.title, 200)) throw new Error(`${at}.title is required`);
|
|
1110
|
+
if (!safeText3(integration.npm, 200)) throw new Error(`${at}.npm is required`);
|
|
1111
|
+
if (integration.schema !== void 0 && (!isRecord6(integration.schema) || !isRecord6(integration.schema.entities))) {
|
|
1007
1112
|
throw new Error(`${at}.schema must contain an entities object`);
|
|
1008
1113
|
}
|
|
1009
|
-
if (integration.rules !== void 0 && !
|
|
1114
|
+
if (integration.rules !== void 0 && !isRecord6(integration.rules)) throw new Error(`${at}.rules must be an object`);
|
|
1010
1115
|
validateSeeds(integration, at);
|
|
1011
1116
|
validateProbes(integration, at);
|
|
1117
|
+
validateSecrets(integration.secrets, at);
|
|
1012
1118
|
}
|
|
1013
1119
|
const needsDb = cfg.integrations.some((integration) => integration.schema || integration.rules || integration.seeds?.length);
|
|
1014
|
-
const services =
|
|
1120
|
+
const services = unique2(cfg.services?.length ? cfg.services : defaultServices);
|
|
1015
1121
|
if (needsDb && !services.includes("db")) throw new Error(`${path}: schema/rules/seed integrations require the db service`);
|
|
1016
1122
|
}
|
|
1123
|
+
var SECRET_NAME = /^\$?[a-z][a-z0-9_]*$/;
|
|
1124
|
+
function validateSecrets(value2, at) {
|
|
1125
|
+
if (value2 === void 0) return;
|
|
1126
|
+
if (!Array.isArray(value2)) throw new Error(`${at}.secrets must be an array`);
|
|
1127
|
+
const names = /* @__PURE__ */ new Set();
|
|
1128
|
+
for (const [index, secret] of value2.entries()) {
|
|
1129
|
+
const sat = `${at}.secrets[${index}]`;
|
|
1130
|
+
if (!isRecord6(secret)) throw new Error(`${sat} must be an object`);
|
|
1131
|
+
if (typeof secret.name !== "string" || !SECRET_NAME.test(secret.name) || secret.name.length > 64) {
|
|
1132
|
+
throw new Error(`${sat}.name must be lowercase snake_case (optionally "$"-prefixed when reserved), e.g. "clerk_webhook_secret"`);
|
|
1133
|
+
}
|
|
1134
|
+
const dollar = secret.name.startsWith("$");
|
|
1135
|
+
if (dollar !== (secret.reserved === true)) {
|
|
1136
|
+
throw new Error(
|
|
1137
|
+
dollar ? `${sat}.name is "$"-prefixed, so it must also set reserved: true` : `${sat} sets reserved: true, so its name must be "$"-prefixed`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
if (!safeText3(secret.description, 500)) throw new Error(`${sat}.description is required \u2014 it is what doctor and the docs show`);
|
|
1141
|
+
if (secret.pattern !== void 0 && !safeText3(secret.pattern, 64)) throw new Error(`${sat}.pattern must be a non-empty prefix string`);
|
|
1142
|
+
if (secret.required !== void 0 && typeof secret.required !== "boolean") throw new Error(`${sat}.required must be a boolean`);
|
|
1143
|
+
if (names.has(secret.name)) throw new Error(`${at} declares secret "${secret.name}" twice`);
|
|
1144
|
+
names.add(secret.name);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1017
1147
|
function validateSeeds(integration, at) {
|
|
1018
1148
|
if (integration.seeds === void 0) return;
|
|
1019
1149
|
if (!Array.isArray(integration.seeds)) throw new Error(`${at}.seeds must be an array`);
|
|
1020
1150
|
const ids = /* @__PURE__ */ new Set();
|
|
1021
1151
|
for (const [index, seed] of integration.seeds.entries()) {
|
|
1022
1152
|
const sat = `${at}.seeds[${index}]`;
|
|
1023
|
-
if (!
|
|
1153
|
+
if (!isRecord6(seed) || !safeText3(seed.id, 200) || !safeText3(seed.ns, 200)) throw new Error(`${sat} requires id and ns`);
|
|
1024
1154
|
if (ids.has(seed.id)) throw new Error(`${at} has duplicate seed id "${seed.id}"`);
|
|
1025
1155
|
ids.add(seed.id);
|
|
1026
|
-
if (!
|
|
1156
|
+
if (!isRecord6(seed.key) || !safeText3(seed.key.attr, 200) || !safeText3(seed.key.value, 2048)) {
|
|
1027
1157
|
throw new Error(`${sat}.key requires string attr and value`);
|
|
1028
1158
|
}
|
|
1029
|
-
if (!
|
|
1159
|
+
if (!isRecord6(seed.attrs)) throw new Error(`${sat}.attrs must be an object`);
|
|
1030
1160
|
if (Object.hasOwn(seed.attrs, seed.key.attr) && seed.attrs[seed.key.attr] !== seed.key.value) {
|
|
1031
1161
|
throw new Error(`${sat}.attrs.${seed.key.attr} conflicts with its natural key`);
|
|
1032
1162
|
}
|
|
@@ -1037,16 +1167,16 @@ function validateProbes(integration, at) {
|
|
|
1037
1167
|
if (!Array.isArray(integration.probes)) throw new Error(`${at}.probes must be an array`);
|
|
1038
1168
|
for (const [index, probe] of integration.probes.entries()) {
|
|
1039
1169
|
const pat = `${at}.probes[${index}]`;
|
|
1040
|
-
if (!
|
|
1170
|
+
if (!isRecord6(probe) || !safeProbePath(probe.path)) throw new Error(`${pat}.path must be an absolute path without query or fragment`);
|
|
1041
1171
|
if (!Number.isInteger(probe.expectedStatus) || probe.expectedStatus < 100 || probe.expectedStatus > 599) {
|
|
1042
1172
|
throw new Error(`${pat}.expectedStatus must be an HTTP status`);
|
|
1043
1173
|
}
|
|
1044
1174
|
}
|
|
1045
1175
|
}
|
|
1046
|
-
function
|
|
1176
|
+
function isRecord6(value2) {
|
|
1047
1177
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1048
1178
|
}
|
|
1049
|
-
function
|
|
1179
|
+
function safeText3(value2, max) {
|
|
1050
1180
|
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1051
1181
|
}
|
|
1052
1182
|
function safeProbePath(value2) {
|
|
@@ -1055,7 +1185,7 @@ function safeProbePath(value2) {
|
|
|
1055
1185
|
function validId(value2) {
|
|
1056
1186
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1057
1187
|
}
|
|
1058
|
-
function
|
|
1188
|
+
function unique2(values) {
|
|
1059
1189
|
return [...new Set(values.filter(Boolean))];
|
|
1060
1190
|
}
|
|
1061
1191
|
|
|
@@ -1075,10 +1205,10 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1075
1205
|
validateRawConfig(raw, resolved);
|
|
1076
1206
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1077
1207
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
1078
|
-
const envs =
|
|
1079
|
-
const services =
|
|
1208
|
+
const envs = unique3(raw.envs?.length ? raw.envs : DEFAULT_ENVS);
|
|
1209
|
+
const services = unique3(raw.services?.length ? raw.services : DEFAULT_SERVICES);
|
|
1080
1210
|
validateServices(services, resolved);
|
|
1081
|
-
validateCalendarConfig(raw,
|
|
1211
|
+
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1082
1212
|
const local = {
|
|
1083
1213
|
tokenFile: resolve2(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1084
1214
|
credentialsFile: resolve2(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
@@ -1126,26 +1256,6 @@ function buildPlan(cfg) {
|
|
|
1126
1256
|
aiProvider: cfg.ai?.provider
|
|
1127
1257
|
};
|
|
1128
1258
|
}
|
|
1129
|
-
function calendarServiceConfig(cfg, env) {
|
|
1130
|
-
if (!cfg.services.includes("calendar")) throw new Error("calendar service is not enabled in config services");
|
|
1131
|
-
if (!cfg.envs.includes(env) && env !== "prod") throw new Error(`calendar env "${env}" is not declared in config envs`);
|
|
1132
|
-
const google = cfg.calendar?.google;
|
|
1133
|
-
if (!google) throw new Error("calendar.google is required when the calendar service is enabled");
|
|
1134
|
-
const configured = google.availabilityCalendars?.[env] ?? google.calendars?.[env];
|
|
1135
|
-
if (!configured?.length) throw new Error(`calendar.google.availabilityCalendars.${env} is required`);
|
|
1136
|
-
const availability = unique2(configured.map((id) => id.trim()));
|
|
1137
|
-
return {
|
|
1138
|
-
provider: "google",
|
|
1139
|
-
access: "book",
|
|
1140
|
-
bookingCalendarId: google.bookingCalendar?.[env]?.trim() ?? availability[0],
|
|
1141
|
-
availabilityCalendars: availability
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
function calendarBookingPageUrl(cfg, env) {
|
|
1145
|
-
const value2 = cfg.calendar?.google.bookingPageUrl?.[env];
|
|
1146
|
-
if (value2 === void 0 || value2 === null) return value2;
|
|
1147
|
-
return new URL(value2).toString();
|
|
1148
|
-
}
|
|
1149
1259
|
function rulesFromSchema(schema) {
|
|
1150
1260
|
const entities = serializedEntities(schema);
|
|
1151
1261
|
return Object.fromEntries(
|
|
@@ -1177,69 +1287,9 @@ function validateRawConfig(raw, path) {
|
|
|
1177
1287
|
throw new Error(`${path}: services must be an array of non-empty names`);
|
|
1178
1288
|
}
|
|
1179
1289
|
validateAiConfig(cfg, path);
|
|
1290
|
+
validateSecrets(cfg.secrets, `${path}: config`);
|
|
1180
1291
|
validateIntegrations(cfg, path, DEFAULT_SERVICES);
|
|
1181
1292
|
}
|
|
1182
|
-
function validateCalendarConfig(cfg, envs, services, path) {
|
|
1183
|
-
const enabled = services.includes("calendar");
|
|
1184
|
-
if (!cfg.calendar) {
|
|
1185
|
-
if (enabled) throw new Error(`${path}: calendar.google is required when services includes "calendar"`);
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
if (!isRecord6(cfg.calendar)) throw new Error(`${path}: calendar must be an object`);
|
|
1189
|
-
assertOnly2(cfg.calendar, ["google"], `${path}: calendar`);
|
|
1190
|
-
if (!isRecord6(cfg.calendar.google)) throw new Error(`${path}: calendar.google must be an object`);
|
|
1191
|
-
const google = cfg.calendar.google;
|
|
1192
|
-
assertOnly2(
|
|
1193
|
-
google,
|
|
1194
|
-
["availabilityCalendars", "calendars", "bookingCalendar", "bookingPageUrl"],
|
|
1195
|
-
`${path}: calendar.google`
|
|
1196
|
-
);
|
|
1197
|
-
const availabilityKey = google.availabilityCalendars !== void 0 ? "availabilityCalendars" : google.calendars !== void 0 ? "calendars" : null;
|
|
1198
|
-
if (!availabilityKey || google.availabilityCalendars !== void 0 && google.calendars !== void 0) {
|
|
1199
|
-
throw new Error(`${path}: calendar.google requires exactly one of availabilityCalendars or calendars (legacy)`);
|
|
1200
|
-
}
|
|
1201
|
-
const availability = google[availabilityKey];
|
|
1202
|
-
if (!isRecord6(availability)) throw new Error(`${path}: calendar.google.${availabilityKey} must map env names to calendar ids`);
|
|
1203
|
-
const unknownEnv = Object.keys(availability).find((env) => !envs.includes(env) && env !== "prod");
|
|
1204
|
-
if (unknownEnv) throw new Error(`${path}: calendar.google.${availabilityKey}.${unknownEnv} is not in config envs`);
|
|
1205
|
-
for (const env of envs) {
|
|
1206
|
-
const ids = availability[env];
|
|
1207
|
-
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1208
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
for (const [env, ids] of Object.entries(availability)) {
|
|
1212
|
-
if (!Array.isArray(ids) || ids.length === 0) {
|
|
1213
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must be a non-empty array`);
|
|
1214
|
-
}
|
|
1215
|
-
if (ids.length > 10) {
|
|
1216
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} must contain at most 10 calendar ids`);
|
|
1217
|
-
}
|
|
1218
|
-
if (ids.some((id) => !safeText3(id, 1024))) {
|
|
1219
|
-
throw new Error(`${path}: calendar.google.${availabilityKey}.${env} contains an invalid calendar id`);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
if (google.bookingCalendar !== void 0) {
|
|
1223
|
-
if (!isRecord6(google.bookingCalendar)) throw new Error(`${path}: calendar.google.bookingCalendar must map env names to one calendar id`);
|
|
1224
|
-
const unknownBookingEnv = Object.keys(google.bookingCalendar).find((env) => !envs.includes(env) && env !== "prod");
|
|
1225
|
-
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingCalendar.${unknownBookingEnv} is not in config envs`);
|
|
1226
|
-
for (const [env, value2] of Object.entries(google.bookingCalendar)) {
|
|
1227
|
-
if (!safeText3(value2, 1024)) {
|
|
1228
|
-
throw new Error(`${path}: calendar.google.bookingCalendar.${env} must be a calendar id`);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
if (google.bookingPageUrl !== void 0) {
|
|
1233
|
-
if (!isRecord6(google.bookingPageUrl)) throw new Error(`${path}: calendar.google.bookingPageUrl must map env names to HTTPS URLs or null`);
|
|
1234
|
-
const unknownBookingEnv = Object.keys(google.bookingPageUrl).find((env) => !envs.includes(env) && env !== "prod");
|
|
1235
|
-
if (unknownBookingEnv) throw new Error(`${path}: calendar.google.bookingPageUrl.${unknownBookingEnv} is not in config envs`);
|
|
1236
|
-
for (const [env, value2] of Object.entries(google.bookingPageUrl)) {
|
|
1237
|
-
if (value2 !== null && !safeHttpsUrl(value2)) {
|
|
1238
|
-
throw new Error(`${path}: calendar.google.bookingPageUrl.${env} must be an HTTPS URL without credentials or fragment`);
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
1293
|
function validateServices(services, path) {
|
|
1244
1294
|
for (const service of services) {
|
|
1245
1295
|
const definition = appServiceDefinition(service);
|
|
@@ -1253,25 +1303,6 @@ function validateServices(services, path) {
|
|
|
1253
1303
|
}
|
|
1254
1304
|
}
|
|
1255
1305
|
}
|
|
1256
|
-
function assertOnly2(value2, allowed, label) {
|
|
1257
|
-
const extra = Object.keys(value2).find((key) => !allowed.includes(key));
|
|
1258
|
-
if (extra) throw new Error(`${label}.${extra} is not supported`);
|
|
1259
|
-
}
|
|
1260
|
-
function isRecord6(value2) {
|
|
1261
|
-
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
1262
|
-
}
|
|
1263
|
-
function safeText3(value2, max) {
|
|
1264
|
-
return typeof value2 === "string" && value2.trim().length > 0 && value2.length <= max && !/[\u0000-\u001f\u007f]/.test(value2);
|
|
1265
|
-
}
|
|
1266
|
-
function safeHttpsUrl(value2) {
|
|
1267
|
-
if (typeof value2 !== "string" || value2.length > 2048) return false;
|
|
1268
|
-
try {
|
|
1269
|
-
const url = new URL(value2);
|
|
1270
|
-
return url.protocol === "https:" && !url.username && !url.password && !url.hash;
|
|
1271
|
-
} catch {
|
|
1272
|
-
return false;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
1306
|
function validId2(value2) {
|
|
1276
1307
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1277
1308
|
}
|
|
@@ -1286,7 +1317,7 @@ async function loadConfigModule(path) {
|
|
|
1286
1317
|
function trimSlash(value2) {
|
|
1287
1318
|
return value2.replace(/\/+$/, "");
|
|
1288
1319
|
}
|
|
1289
|
-
function
|
|
1320
|
+
function unique3(values) {
|
|
1290
1321
|
return [...new Set(values.filter(Boolean))];
|
|
1291
1322
|
}
|
|
1292
1323
|
|
|
@@ -4068,6 +4099,67 @@ function isRecord7(value2) {
|
|
|
4068
4099
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
4069
4100
|
}
|
|
4070
4101
|
|
|
4102
|
+
// src/secret-contract.ts
|
|
4103
|
+
var APP_SOURCE = "app";
|
|
4104
|
+
function resolveSecretContract(cfg) {
|
|
4105
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4106
|
+
const declarations = [
|
|
4107
|
+
...(cfg.secrets ?? []).map((secret) => ({ source: APP_SOURCE, secret })),
|
|
4108
|
+
...(cfg.integrations ?? []).flatMap(
|
|
4109
|
+
(integration) => (integration.secrets ?? []).map((secret) => ({ source: integration.id, secret }))
|
|
4110
|
+
)
|
|
4111
|
+
];
|
|
4112
|
+
for (const { source, secret } of declarations) {
|
|
4113
|
+
const existing = byName.get(secret.name);
|
|
4114
|
+
if (!existing) {
|
|
4115
|
+
byName.set(secret.name, { ...secret, required: secret.required !== false, sources: [source] });
|
|
4116
|
+
continue;
|
|
4117
|
+
}
|
|
4118
|
+
existing.sources.push(source);
|
|
4119
|
+
existing.required = existing.required || secret.required !== false;
|
|
4120
|
+
existing.pattern ??= secret.pattern;
|
|
4121
|
+
}
|
|
4122
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
4123
|
+
}
|
|
4124
|
+
function secretContractWarnings(contract, cfg) {
|
|
4125
|
+
const warnings = [];
|
|
4126
|
+
const declaredPatterns = /* @__PURE__ */ new Map();
|
|
4127
|
+
for (const integration of cfg.integrations ?? []) {
|
|
4128
|
+
for (const secret of integration.secrets ?? []) {
|
|
4129
|
+
if (!secret.pattern) continue;
|
|
4130
|
+
const seen = declaredPatterns.get(secret.name) ?? /* @__PURE__ */ new Map();
|
|
4131
|
+
seen.set(integration.id, secret.pattern);
|
|
4132
|
+
declaredPatterns.set(secret.name, seen);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
for (const secret of cfg.secrets ?? []) {
|
|
4136
|
+
if (!secret.pattern) continue;
|
|
4137
|
+
const seen = declaredPatterns.get(secret.name) ?? /* @__PURE__ */ new Map();
|
|
4138
|
+
seen.set(APP_SOURCE, secret.pattern);
|
|
4139
|
+
declaredPatterns.set(secret.name, seen);
|
|
4140
|
+
}
|
|
4141
|
+
for (const [name, seen] of declaredPatterns) {
|
|
4142
|
+
const distinct = [...new Set(seen.values())];
|
|
4143
|
+
if (distinct.length > 1) {
|
|
4144
|
+
const detail = [...seen].map(([source, pattern]) => `${source} expects "${pattern}"`).join(", ");
|
|
4145
|
+
warnings.push(`secret "${name}" has conflicting patterns \u2014 ${detail}; one of them will reject a valid value`);
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
if (contract.length > 0 && !cfg.services.includes("db")) {
|
|
4149
|
+
const names = contract.map((secret) => secret.name).join(", ");
|
|
4150
|
+
warnings.push(`secrets are declared (${names}) but the db service is off \u2014 nothing can read the tenant vault`);
|
|
4151
|
+
}
|
|
4152
|
+
return warnings;
|
|
4153
|
+
}
|
|
4154
|
+
function formatSecretContract(contract) {
|
|
4155
|
+
return contract.map((secret) => {
|
|
4156
|
+
const flags = [secret.required ? "required" : "optional"];
|
|
4157
|
+
if (secret.reserved) flags.push("reserved");
|
|
4158
|
+
if (secret.pattern) flags.push(`${secret.pattern}\u2026`);
|
|
4159
|
+
return ` ${secret.name} (${flags.join(", ")}) \u2014 ${secret.sources.join(", ")}`;
|
|
4160
|
+
});
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4071
4163
|
// src/doctor.ts
|
|
4072
4164
|
async function doctor(options) {
|
|
4073
4165
|
const out = options.stdout ?? console;
|
|
@@ -4084,6 +4176,9 @@ async function doctor(options) {
|
|
|
4084
4176
|
out.log(`schema: ${schema ? `${entities.length} entities` : "none"}`);
|
|
4085
4177
|
out.log(`rules: ${rules ? `${Object.keys(rules).length} namespaces` : "none"}`);
|
|
4086
4178
|
out.log(`ai: ${cfg.services.includes("ai") ? cfg.ai?.provider ? `byok/${cfg.ai.provider}` : "hosted" : "not enabled"}`);
|
|
4179
|
+
const contract = resolveSecretContract(cfg);
|
|
4180
|
+
out.log(`secrets: ${contract.length ? `${contract.length} declared` : "none declared"}`);
|
|
4181
|
+
for (const line of formatSecretContract(contract)) out.log(line);
|
|
4087
4182
|
if (cfg.services.includes("calendar")) {
|
|
4088
4183
|
const calendar = cfg.envs.map((env) => {
|
|
4089
4184
|
const resolved = calendarServiceConfig(cfg, env);
|
|
@@ -4104,6 +4199,7 @@ async function doctor(options) {
|
|
|
4104
4199
|
}
|
|
4105
4200
|
}
|
|
4106
4201
|
warnings.push(...integrationWarnings(database.integrations, schema, rules));
|
|
4202
|
+
warnings.push(...secretContractWarnings(contract, cfg));
|
|
4107
4203
|
if (cfg.services.includes("ai") && cfg.ai?.mode === "byok" && !cfg.ai.provider) {
|
|
4108
4204
|
warnings.push("ai.mode is byok but ai.provider is not set");
|
|
4109
4205
|
}
|
|
@@ -4430,6 +4526,71 @@ async function resolveVaultWrite(options) {
|
|
|
4430
4526
|
return { cfg, tenantId: tenantIdFor3(cfg.app.id, env), value: value2, doFetch, out };
|
|
4431
4527
|
}
|
|
4432
4528
|
|
|
4529
|
+
// src/secrets-status.ts
|
|
4530
|
+
import { tenantIdFor as tenantIdFor4 } from "@odla-ai/apps";
|
|
4531
|
+
async function secretsStatus(options) {
|
|
4532
|
+
const out = options.stdout ?? console;
|
|
4533
|
+
const doFetch = options.fetch ?? fetch;
|
|
4534
|
+
const cfg = await loadProjectConfig(options.configPath);
|
|
4535
|
+
if (!cfg.envs.includes(options.env)) {
|
|
4536
|
+
throw new Error(`env "${options.env}" is not in config envs (${cfg.envs.join(", ")})`);
|
|
4537
|
+
}
|
|
4538
|
+
const tenantId = tenantIdFor4(cfg.app.id, options.env);
|
|
4539
|
+
const contract = resolveSecretContract(cfg);
|
|
4540
|
+
const token = await getDeveloperToken(cfg, options, doFetch, out);
|
|
4541
|
+
const res = await doFetch(`${cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/secrets`, {
|
|
4542
|
+
headers: { authorization: `Bearer ${token}` }
|
|
4543
|
+
});
|
|
4544
|
+
if (!res.ok) {
|
|
4545
|
+
const detail = (await res.text().catch(() => "")).slice(0, 300);
|
|
4546
|
+
throw new Error(`list secrets for ${tenantId} failed (${res.status}): ${detail || "request failed"}`);
|
|
4547
|
+
}
|
|
4548
|
+
const body = await res.json();
|
|
4549
|
+
const stored = new Set((body.secrets ?? []).map((entry) => String(entry.name)));
|
|
4550
|
+
const report4 = buildReport(cfg.app.id, options.env, tenantId, contract, stored);
|
|
4551
|
+
if (options.json) out.log(JSON.stringify(report4, null, 2));
|
|
4552
|
+
else printReport(report4, out);
|
|
4553
|
+
return report4;
|
|
4554
|
+
}
|
|
4555
|
+
function buildReport(appId, env, tenant, contract, stored) {
|
|
4556
|
+
const declared = new Set(contract.map((secret) => secret.name));
|
|
4557
|
+
const rows = contract.map((secret) => ({
|
|
4558
|
+
name: secret.name,
|
|
4559
|
+
state: secret.reserved ? "reserved" : stored.has(secret.name) ? "set" : "missing",
|
|
4560
|
+
required: secret.required,
|
|
4561
|
+
sources: secret.sources,
|
|
4562
|
+
description: secret.description
|
|
4563
|
+
}));
|
|
4564
|
+
for (const name of [...stored].sort()) {
|
|
4565
|
+
if (!declared.has(name)) rows.push({ name, state: "undeclared", required: false, sources: [] });
|
|
4566
|
+
}
|
|
4567
|
+
const ok = rows.every((row) => row.state !== "missing" || !row.required);
|
|
4568
|
+
return { app: appId, env, tenant, secrets: rows, ok };
|
|
4569
|
+
}
|
|
4570
|
+
function printReport(report4, out) {
|
|
4571
|
+
out.log(`${report4.app} (${report4.tenant})`);
|
|
4572
|
+
if (report4.secrets.length === 0) {
|
|
4573
|
+
out.log(" no secrets declared and none stored");
|
|
4574
|
+
return;
|
|
4575
|
+
}
|
|
4576
|
+
for (const row of report4.secrets) {
|
|
4577
|
+
const label = row.state === "missing" && !row.required ? "missing (optional)" : row.state;
|
|
4578
|
+
const suffix = row.sources.length ? ` \u2014 ${row.sources.join(", ")}` : "";
|
|
4579
|
+
out.log(` ${label.padEnd(18)} ${row.name}${suffix}`);
|
|
4580
|
+
}
|
|
4581
|
+
const missing = report4.secrets.filter((row) => row.state === "missing" && row.required);
|
|
4582
|
+
if (missing.length) {
|
|
4583
|
+
out.log("");
|
|
4584
|
+
for (const row of missing) {
|
|
4585
|
+
out.log(`${row.name} is required but not set \u2014 "odla-ai secrets set ${row.name} --env ${report4.env} --stdin"`);
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
if (report4.secrets.some((row) => row.state === "reserved")) {
|
|
4589
|
+
out.log("");
|
|
4590
|
+
out.log('"reserved" slots are never enumerated by the vault; presence cannot be confirmed here.');
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4433
4594
|
// src/skill.ts
|
|
4434
4595
|
import { existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
4435
4596
|
import { homedir as homedir2 } from "os";
|
|
@@ -4899,9 +5060,22 @@ async function secretsCommand(parsed, deps) {
|
|
|
4899
5060
|
await (sub === "set" ? secretsSet(options) : secretsSetClerkKey(options));
|
|
4900
5061
|
return;
|
|
4901
5062
|
}
|
|
5063
|
+
if (sub === "status") {
|
|
5064
|
+
assertArgs(parsed, ["config", "env", "token", "email", "json"], 2);
|
|
5065
|
+
await secretsStatus({
|
|
5066
|
+
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
5067
|
+
env: requiredString(parsed.options.env, "--env"),
|
|
5068
|
+
json: parsed.options.json === true,
|
|
5069
|
+
token: stringOpt(parsed.options.token),
|
|
5070
|
+
email: stringOpt(parsed.options.email),
|
|
5071
|
+
fetch: deps.fetch,
|
|
5072
|
+
stdout: deps.stdout
|
|
5073
|
+
});
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
4902
5076
|
if (sub !== "push") {
|
|
4903
5077
|
throw new Error(
|
|
4904
|
-
`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".`
|
|
5078
|
+
`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".`
|
|
4905
5079
|
);
|
|
4906
5080
|
}
|
|
4907
5081
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
@@ -5425,7 +5599,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
5425
5599
|
}
|
|
5426
5600
|
}
|
|
5427
5601
|
|
|
5428
|
-
// ../harness/dist/chunk-
|
|
5602
|
+
// ../harness/dist/chunk-ANNX7VGK.js
|
|
5429
5603
|
import { createHash as createHash3 } from "crypto";
|
|
5430
5604
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
5431
5605
|
import { relative as relative4, resolve as resolve10 } from "path";
|
|
@@ -5485,9 +5659,9 @@ function dependenciesOf(values, influence = "data") {
|
|
|
5485
5659
|
result.push({ ref, influence, promptSafetyAtUse: value2.label.promptSafety });
|
|
5486
5660
|
}
|
|
5487
5661
|
}
|
|
5488
|
-
const
|
|
5489
|
-
for (const dep of result)
|
|
5490
|
-
return [...
|
|
5662
|
+
const unique4 = /* @__PURE__ */ new Map();
|
|
5663
|
+
for (const dep of result) unique4.set(`${dep.ref.kind}\0${dep.ref.id}\0${dep.influence}\0${dep.promptSafetyAtUse}`, dep);
|
|
5664
|
+
return [...unique4.values()];
|
|
5491
5665
|
}
|
|
5492
5666
|
|
|
5493
5667
|
// ../camel/dist/chunk-4DQ6BIHP.js
|
|
@@ -5762,7 +5936,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
5762
5936
|
}
|
|
5763
5937
|
}
|
|
5764
5938
|
|
|
5765
|
-
// ../harness/dist/chunk-
|
|
5939
|
+
// ../harness/dist/chunk-ANNX7VGK.js
|
|
5766
5940
|
import { spawn as spawn4 } from "child_process";
|
|
5767
5941
|
import { lstat as lstat2 } from "fs/promises";
|
|
5768
5942
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -6067,7 +6241,7 @@ function looksLikeDestination(value2) {
|
|
|
6067
6241
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text2) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text2);
|
|
6068
6242
|
}
|
|
6069
6243
|
|
|
6070
|
-
// ../harness/dist/chunk-
|
|
6244
|
+
// ../harness/dist/chunk-ANNX7VGK.js
|
|
6071
6245
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
6072
6246
|
import { readFile as readFile3 } from "fs/promises";
|
|
6073
6247
|
import { join as join33 } from "path";
|
|
@@ -6335,7 +6509,7 @@ async function buildCodeGraph(input) {
|
|
|
6335
6509
|
return builder.build();
|
|
6336
6510
|
}
|
|
6337
6511
|
|
|
6338
|
-
// ../harness/dist/chunk-
|
|
6512
|
+
// ../harness/dist/chunk-ANNX7VGK.js
|
|
6339
6513
|
import { createHash as createHash32 } from "crypto";
|
|
6340
6514
|
async function digestStagedWorkspace(root, limits) {
|
|
6341
6515
|
const files = [];
|
|
@@ -6522,6 +6696,16 @@ function createCodeRuntimeControlClient(options) {
|
|
|
6522
6696
|
}
|
|
6523
6697
|
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/chat/events`, { eventId, event });
|
|
6524
6698
|
},
|
|
6699
|
+
recallMemories: async (sessionId, subjects, limit) => {
|
|
6700
|
+
const response2 = await call2(
|
|
6701
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/recall`,
|
|
6702
|
+
{ subjects: [...subjects], limit }
|
|
6703
|
+
);
|
|
6704
|
+
return Array.isArray(response2.memories) ? response2.memories : [];
|
|
6705
|
+
},
|
|
6706
|
+
rememberMemory: async (sessionId, memory) => {
|
|
6707
|
+
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/remember`, memory);
|
|
6708
|
+
},
|
|
6525
6709
|
reportSessionFailure: async (sessionId, message2) => {
|
|
6526
6710
|
if (!message2.trim() || message2.length > 2e3) throw new TypeError("invalid Code session failure");
|
|
6527
6711
|
await call2(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/failure`, { message: message2 });
|
|
@@ -8216,6 +8400,29 @@ function validateOptions(options) {
|
|
|
8216
8400
|
throw new TypeError("Code tool broker read-only prefix is invalid");
|
|
8217
8401
|
}
|
|
8218
8402
|
}
|
|
8403
|
+
var MAX_MEMORY_BODY = 4e3;
|
|
8404
|
+
function validateMemory(memory) {
|
|
8405
|
+
if (!memory.subject.includes(":")) {
|
|
8406
|
+
throw new TypeError(`memory subject must be a graph node id, got "${memory.subject}"`);
|
|
8407
|
+
}
|
|
8408
|
+
const body = memory.body.trim();
|
|
8409
|
+
if (!body) throw new TypeError("a memory needs a body");
|
|
8410
|
+
if (body.length > MAX_MEMORY_BODY) throw new TypeError("memory body exceeds its bound");
|
|
8411
|
+
if (!memory.authorId.trim()) throw new TypeError("a memory needs an author");
|
|
8412
|
+
}
|
|
8413
|
+
function hazardFromAttempt(input) {
|
|
8414
|
+
const body = [
|
|
8415
|
+
`Attempt ${input.attempt} at "${input.goal.slice(0, 200)}" failed its proof.`,
|
|
8416
|
+
input.feedback.replace(/\s+/g, " ").slice(0, MAX_MEMORY_BODY - 300)
|
|
8417
|
+
].join(" ");
|
|
8418
|
+
return input.touched.slice(0, 10).map((path) => ({
|
|
8419
|
+
subject: path.includes(":") ? path : `file:${path}`,
|
|
8420
|
+
kind: "hazard",
|
|
8421
|
+
body,
|
|
8422
|
+
evidence: { kind: "gate", ref: input.verificationId },
|
|
8423
|
+
authorId: input.authorId
|
|
8424
|
+
}));
|
|
8425
|
+
}
|
|
8219
8426
|
async function runGoal(spec, attempt) {
|
|
8220
8427
|
assertBudget(spec.budget);
|
|
8221
8428
|
const now = spec.now ?? Date.now;
|
|
@@ -8407,6 +8614,9 @@ function pursueRuntimeGoal(input) {
|
|
|
8407
8614
|
return { gatePassed: false, feedback: "", tokens: outcome.tokens, error: outcome.error };
|
|
8408
8615
|
}
|
|
8409
8616
|
const verdict = await input.gate(attempt);
|
|
8617
|
+
if (!verdict.passed && input.memory) {
|
|
8618
|
+
await rememberFailure(input, attempt, verdict.feedback);
|
|
8619
|
+
}
|
|
8410
8620
|
return {
|
|
8411
8621
|
gatePassed: verdict.passed,
|
|
8412
8622
|
feedback: verdict.feedback,
|
|
@@ -8417,6 +8627,25 @@ function pursueRuntimeGoal(input) {
|
|
|
8417
8627
|
}
|
|
8418
8628
|
);
|
|
8419
8629
|
}
|
|
8630
|
+
async function rememberFailure(input, attempt, feedback) {
|
|
8631
|
+
if (!input.memory || !feedback.trim()) return;
|
|
8632
|
+
try {
|
|
8633
|
+
const touched = await input.touched?.(attempt) ?? [];
|
|
8634
|
+
if (touched.length === 0) return;
|
|
8635
|
+
for (const memory of hazardFromAttempt({
|
|
8636
|
+
goal: input.spec.goal,
|
|
8637
|
+
attempt,
|
|
8638
|
+
feedback,
|
|
8639
|
+
touched,
|
|
8640
|
+
verificationId: `goal-${attempt}`,
|
|
8641
|
+
authorId: input.memory.authorId
|
|
8642
|
+
})) {
|
|
8643
|
+
validateMemory(memory);
|
|
8644
|
+
await input.memory.store.remember(memory);
|
|
8645
|
+
}
|
|
8646
|
+
} catch {
|
|
8647
|
+
}
|
|
8648
|
+
}
|
|
8420
8649
|
function goalEventLine(event) {
|
|
8421
8650
|
if (event.type === "attempt_started") return `Goal attempt ${event.attempt} starting.`;
|
|
8422
8651
|
if (event.type === "attempt_failed") return `Attempt ${event.attempt} did not satisfy the proof.`;
|
|
@@ -9681,7 +9910,9 @@ Commands:
|
|
|
9681
9910
|
copilot, gemini, or agents (repeatable or comma-separated).
|
|
9682
9911
|
secrets Push configured db/o11y secrets into the Worker via wrangler
|
|
9683
9912
|
stdin; set stores a tenant-vault secret and set-clerk-key the
|
|
9684
|
-
reserved Clerk secret key, write-only from stdin or an env var
|
|
9913
|
+
reserved Clerk secret key, write-only from stdin or an env var;
|
|
9914
|
+
status compares the secrets the config declares against the
|
|
9915
|
+
names the environment's vault holds (--json for a report).
|
|
9685
9916
|
version Print the CLI version.
|
|
9686
9917
|
|
|
9687
9918
|
Safety:
|
|
@@ -11664,7 +11895,7 @@ async function read2(url, headers, doFetch) {
|
|
|
11664
11895
|
}
|
|
11665
11896
|
|
|
11666
11897
|
// src/provision.ts
|
|
11667
|
-
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as
|
|
11898
|
+
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
|
|
11668
11899
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
11669
11900
|
import process13 from "process";
|
|
11670
11901
|
|
|
@@ -11721,9 +11952,9 @@ async function responseText(res) {
|
|
|
11721
11952
|
}
|
|
11722
11953
|
|
|
11723
11954
|
// src/provision-credentials.ts
|
|
11724
|
-
import { tenantIdFor as
|
|
11955
|
+
import { tenantIdFor as tenantIdFor5 } from "@odla-ai/apps";
|
|
11725
11956
|
async function provisionEnvCredentials(opts) {
|
|
11726
|
-
const tenantId =
|
|
11957
|
+
const tenantId = tenantIdFor5(opts.cfg.app.id, opts.env);
|
|
11727
11958
|
const prior = opts.credentials?.envs[opts.env];
|
|
11728
11959
|
let credentials = opts.credentials;
|
|
11729
11960
|
let dbKey = opts.cfg.services.includes("db") && !opts.rotateDb ? prior?.dbKey : void 0;
|
|
@@ -12055,7 +12286,7 @@ async function provision(options) {
|
|
|
12055
12286
|
}
|
|
12056
12287
|
let devVarsCredentials = credentials;
|
|
12057
12288
|
for (const env of cfg.envs) {
|
|
12058
|
-
const tenantId =
|
|
12289
|
+
const tenantId = tenantIdFor6(cfg.app.id, env);
|
|
12059
12290
|
let dbKey;
|
|
12060
12291
|
if (options.pushSecrets) {
|
|
12061
12292
|
const delivered = await deliverRuntimeCredentials(cfg, {
|
|
@@ -12257,7 +12488,7 @@ var COMMAND_SURFACE = {
|
|
|
12257
12488
|
rm: {},
|
|
12258
12489
|
lint: {}
|
|
12259
12490
|
},
|
|
12260
|
-
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
12491
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
12261
12492
|
security: {
|
|
12262
12493
|
plan: {},
|
|
12263
12494
|
sources: {},
|
|
@@ -14032,9 +14263,9 @@ export {
|
|
|
14032
14263
|
getScopedPlatformToken,
|
|
14033
14264
|
SYSTEM_AI_PURPOSES,
|
|
14034
14265
|
adminAi,
|
|
14035
|
-
GOOGLE_CALENDAR_EVENTS_SCOPE,
|
|
14036
14266
|
calendarServiceConfig,
|
|
14037
14267
|
calendarBookingPageUrl,
|
|
14268
|
+
GOOGLE_CALENDAR_EVENTS_SCOPE,
|
|
14038
14269
|
calendarStatus,
|
|
14039
14270
|
calendarCalendars,
|
|
14040
14271
|
calendarConnect,
|
|
@@ -14084,4 +14315,4 @@ export {
|
|
|
14084
14315
|
isTerminalHostedSecurityStatus,
|
|
14085
14316
|
runCli
|
|
14086
14317
|
};
|
|
14087
|
-
//# sourceMappingURL=chunk-
|
|
14318
|
+
//# sourceMappingURL=chunk-YSQORU5J.js.map
|