@odla-ai/cli 0.43.0 → 0.44.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/README.md +10 -4
- package/dist/bin.cjs +537 -396
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-WDWJ7HC7.js → chunk-7MUWCSGP.js} +517 -399
- package/dist/chunk-7MUWCSGP.js.map +1 -0
- package/dist/{cli-OITEPXW4.js → cli-ZQBZT6YF.js} +2 -2
- package/dist/index.cjs +510 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +10 -3
- package/skills/odla/references/pm-work-intake.md +33 -11
- package/skills/odla/references/pm.md +23 -12
- package/dist/chunk-WDWJ7HC7.js.map +0 -1
- /package/dist/{cli-OITEPXW4.js.map → cli-ZQBZT6YF.js.map} +0 -0
|
@@ -365,10 +365,10 @@ function isManagedDevVar(line2) {
|
|
|
365
365
|
const match = line2.match(/^\s*(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/);
|
|
366
366
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
367
367
|
}
|
|
368
|
-
function writePrivateText(path,
|
|
368
|
+
function writePrivateText(path, text4) {
|
|
369
369
|
mkdirSync2(dirname3(path), { recursive: true });
|
|
370
370
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
371
|
-
writeFileSync(temporary,
|
|
371
|
+
writeFileSync(temporary, text4, { mode: 384 });
|
|
372
372
|
chmodSync2(temporary, 384);
|
|
373
373
|
renameSync(temporary, path);
|
|
374
374
|
}
|
|
@@ -808,12 +808,12 @@ async function readAdminAiAudit(request3) {
|
|
|
808
808
|
}
|
|
809
809
|
}
|
|
810
810
|
async function responseBody(response2) {
|
|
811
|
-
const
|
|
812
|
-
if (!
|
|
811
|
+
const text4 = await response2.text();
|
|
812
|
+
if (!text4) return {};
|
|
813
813
|
try {
|
|
814
|
-
return JSON.parse(
|
|
814
|
+
return JSON.parse(text4);
|
|
815
815
|
} catch {
|
|
816
|
-
return { message:
|
|
816
|
+
return { message: text4.slice(0, 300) };
|
|
817
817
|
}
|
|
818
818
|
}
|
|
819
819
|
function apiError(status, body) {
|
|
@@ -915,12 +915,12 @@ function timestamp2(value2) {
|
|
|
915
915
|
return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
|
|
916
916
|
}
|
|
917
917
|
async function responseBody2(res) {
|
|
918
|
-
const
|
|
919
|
-
if (!
|
|
918
|
+
const text4 = await res.text();
|
|
919
|
+
if (!text4) return {};
|
|
920
920
|
try {
|
|
921
|
-
return JSON.parse(
|
|
921
|
+
return JSON.parse(text4);
|
|
922
922
|
} catch {
|
|
923
|
-
return { message:
|
|
923
|
+
return { message: text4.slice(0, 300) };
|
|
924
924
|
}
|
|
925
925
|
}
|
|
926
926
|
function apiError2(action2, status, body) {
|
|
@@ -1096,12 +1096,12 @@ function catalogModels(body) {
|
|
|
1096
1096
|
return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
|
|
1097
1097
|
}
|
|
1098
1098
|
async function responseBody3(res) {
|
|
1099
|
-
const
|
|
1100
|
-
if (!
|
|
1099
|
+
const text4 = await res.text();
|
|
1100
|
+
if (!text4) return {};
|
|
1101
1101
|
try {
|
|
1102
|
-
return JSON.parse(
|
|
1102
|
+
return JSON.parse(text4);
|
|
1103
1103
|
} catch {
|
|
1104
|
-
return { message:
|
|
1104
|
+
return { message: text4.slice(0, 300) };
|
|
1105
1105
|
}
|
|
1106
1106
|
}
|
|
1107
1107
|
function apiError3(action2, status, body) {
|
|
@@ -1147,10 +1147,17 @@ function parseArgv(argv) {
|
|
|
1147
1147
|
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
1148
1148
|
const allowed = new Set(allowedOptions2);
|
|
1149
1149
|
for (const name of Object.keys(parsed.options)) {
|
|
1150
|
-
if (
|
|
1150
|
+
if (allowed.has(name)) continue;
|
|
1151
|
+
const accepts = [...allowed].sort().map((option) => `--${option}`).join(" ");
|
|
1152
|
+
throw new Error(
|
|
1153
|
+
`unknown option "--${name}"` + (accepts ? ` \u2014 this command accepts: ${accepts}` : " \u2014 this command takes no options")
|
|
1154
|
+
);
|
|
1151
1155
|
}
|
|
1152
1156
|
if (parsed.positionals.length > maxPositionals) {
|
|
1153
|
-
|
|
1157
|
+
const taken = parsed.positionals.slice(0, maxPositionals).join(" ");
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`unexpected argument "${parsed.positionals[maxPositionals]}" \u2014 ` + (maxPositionals ? `"odla-ai ${taken}" takes no further arguments` : "this command takes no arguments")
|
|
1160
|
+
);
|
|
1154
1161
|
}
|
|
1155
1162
|
}
|
|
1156
1163
|
function requiredString(value2, name) {
|
|
@@ -1188,6 +1195,198 @@ function addOption(options, name, value2) {
|
|
|
1188
1195
|
else options[name] = [String(current), String(value2)];
|
|
1189
1196
|
}
|
|
1190
1197
|
|
|
1198
|
+
// src/surface.ts
|
|
1199
|
+
var PM_ACTIONS = {
|
|
1200
|
+
list: {},
|
|
1201
|
+
add: {},
|
|
1202
|
+
create: {},
|
|
1203
|
+
get: {},
|
|
1204
|
+
set: {},
|
|
1205
|
+
update: {},
|
|
1206
|
+
status: {},
|
|
1207
|
+
move: {},
|
|
1208
|
+
done: {},
|
|
1209
|
+
comment: {},
|
|
1210
|
+
comments: {},
|
|
1211
|
+
history: {},
|
|
1212
|
+
link: {},
|
|
1213
|
+
ref: {},
|
|
1214
|
+
rm: {},
|
|
1215
|
+
delete: {}
|
|
1216
|
+
};
|
|
1217
|
+
var PM_TASK_ACTIONS = {
|
|
1218
|
+
...PM_ACTIONS,
|
|
1219
|
+
ready: {},
|
|
1220
|
+
claim: {},
|
|
1221
|
+
release: {}
|
|
1222
|
+
};
|
|
1223
|
+
var PM_ENTITIES = {
|
|
1224
|
+
...Object.fromEntries(
|
|
1225
|
+
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
1226
|
+
),
|
|
1227
|
+
task: PM_TASK_ACTIONS,
|
|
1228
|
+
kanban: PM_TASK_ACTIONS
|
|
1229
|
+
};
|
|
1230
|
+
var COMMAND_SURFACE = {
|
|
1231
|
+
agent: { jobs: {}, retry: {} },
|
|
1232
|
+
ai: { models: {} },
|
|
1233
|
+
admin: {
|
|
1234
|
+
ai: {
|
|
1235
|
+
show: {},
|
|
1236
|
+
set: {},
|
|
1237
|
+
credentials: {},
|
|
1238
|
+
models: {},
|
|
1239
|
+
usage: {},
|
|
1240
|
+
audit: {},
|
|
1241
|
+
credential: { set: {} }
|
|
1242
|
+
},
|
|
1243
|
+
spend: { show: {}, reset: {} }
|
|
1244
|
+
},
|
|
1245
|
+
app: {
|
|
1246
|
+
archive: {},
|
|
1247
|
+
restore: {},
|
|
1248
|
+
export: {},
|
|
1249
|
+
import: {},
|
|
1250
|
+
rename: {},
|
|
1251
|
+
"refresh-sandbox": {},
|
|
1252
|
+
"go-live": {},
|
|
1253
|
+
promote: {},
|
|
1254
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
1255
|
+
},
|
|
1256
|
+
auth: { login: {} },
|
|
1257
|
+
brand: { design: { unpack: {} } },
|
|
1258
|
+
bug: { create: {}, list: {}, report: {} },
|
|
1259
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
1260
|
+
capabilities: {},
|
|
1261
|
+
code: {
|
|
1262
|
+
connect: {},
|
|
1263
|
+
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
1264
|
+
repository: { show: {}, list: {}, bind: {} }
|
|
1265
|
+
},
|
|
1266
|
+
config: { diff: {}, plan: {}, apply: {} },
|
|
1267
|
+
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
1268
|
+
credentials: { list: {}, revoke: {} },
|
|
1269
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
1270
|
+
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
1271
|
+
discuss: {
|
|
1272
|
+
groups: {},
|
|
1273
|
+
list: {},
|
|
1274
|
+
topics: {},
|
|
1275
|
+
read: {},
|
|
1276
|
+
post: {},
|
|
1277
|
+
reply: {},
|
|
1278
|
+
resolve: {},
|
|
1279
|
+
who: {},
|
|
1280
|
+
watch: {}
|
|
1281
|
+
},
|
|
1282
|
+
doctor: {},
|
|
1283
|
+
help: {},
|
|
1284
|
+
init: {},
|
|
1285
|
+
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
1286
|
+
o11y: { status: {} },
|
|
1287
|
+
operations: { get: {}, wait: {} },
|
|
1288
|
+
platform: {
|
|
1289
|
+
status: {}
|
|
1290
|
+
},
|
|
1291
|
+
pm: {
|
|
1292
|
+
...PM_ENTITIES,
|
|
1293
|
+
project: { list: {}, add: {}, create: {}, use: {} },
|
|
1294
|
+
handoff: {},
|
|
1295
|
+
next: {},
|
|
1296
|
+
start: {},
|
|
1297
|
+
watch: {}
|
|
1298
|
+
},
|
|
1299
|
+
provision: {},
|
|
1300
|
+
runbook: {
|
|
1301
|
+
ask: {},
|
|
1302
|
+
search: {},
|
|
1303
|
+
impact: {},
|
|
1304
|
+
list: {},
|
|
1305
|
+
get: {},
|
|
1306
|
+
cat: {},
|
|
1307
|
+
new: {},
|
|
1308
|
+
edit: {},
|
|
1309
|
+
comment: {},
|
|
1310
|
+
import: {},
|
|
1311
|
+
visibility: {},
|
|
1312
|
+
publish: {},
|
|
1313
|
+
archive: {},
|
|
1314
|
+
history: {},
|
|
1315
|
+
revert: {},
|
|
1316
|
+
rm: {},
|
|
1317
|
+
lint: {}
|
|
1318
|
+
},
|
|
1319
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
1320
|
+
security: {
|
|
1321
|
+
plan: {},
|
|
1322
|
+
sources: {},
|
|
1323
|
+
run: {},
|
|
1324
|
+
status: {},
|
|
1325
|
+
report: {},
|
|
1326
|
+
github: { connect: {}, disconnect: {} }
|
|
1327
|
+
},
|
|
1328
|
+
setup: {},
|
|
1329
|
+
skill: { install: {} },
|
|
1330
|
+
smoke: {},
|
|
1331
|
+
version: {},
|
|
1332
|
+
whoami: {}
|
|
1333
|
+
};
|
|
1334
|
+
function acceptedAfter(path) {
|
|
1335
|
+
let node = COMMAND_SURFACE;
|
|
1336
|
+
for (const word of path) {
|
|
1337
|
+
node = node?.[word];
|
|
1338
|
+
if (!node) return [];
|
|
1339
|
+
}
|
|
1340
|
+
return Object.keys(node).sort();
|
|
1341
|
+
}
|
|
1342
|
+
function validateInvocation(words2) {
|
|
1343
|
+
let node = COMMAND_SURFACE;
|
|
1344
|
+
const walked = [];
|
|
1345
|
+
for (const word of words2) {
|
|
1346
|
+
if (Object.keys(node).length === 0) return null;
|
|
1347
|
+
const next = node[word];
|
|
1348
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
1349
|
+
walked.push(word);
|
|
1350
|
+
node = next;
|
|
1351
|
+
}
|
|
1352
|
+
return null;
|
|
1353
|
+
}
|
|
1354
|
+
function describeProblem(problem) {
|
|
1355
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
1356
|
+
if (!problem.accepted.length) return `"${problem.word}" is not accepted ${where}. Run "odla-ai help"`;
|
|
1357
|
+
if (!problem.word) return `"odla-ai ${problem.validPrefix}" needs one of: ${problem.accepted.join(", ")}`;
|
|
1358
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
1359
|
+
}
|
|
1360
|
+
function rejectWord(path, word, note) {
|
|
1361
|
+
const sentence = describeProblem({
|
|
1362
|
+
validPrefix: path.join(" "),
|
|
1363
|
+
word: word ?? "",
|
|
1364
|
+
accepted: acceptedAfter(path)
|
|
1365
|
+
});
|
|
1366
|
+
throw new Error(note ? `${sentence}. ${note}` : sentence);
|
|
1367
|
+
}
|
|
1368
|
+
function invocationPath(words2) {
|
|
1369
|
+
let node = COMMAND_SURFACE;
|
|
1370
|
+
const path = [];
|
|
1371
|
+
for (const word of words2) {
|
|
1372
|
+
const next = node[word];
|
|
1373
|
+
if (!next) break;
|
|
1374
|
+
path.push(word);
|
|
1375
|
+
node = next;
|
|
1376
|
+
if (Object.keys(node).length === 0) break;
|
|
1377
|
+
}
|
|
1378
|
+
return path;
|
|
1379
|
+
}
|
|
1380
|
+
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
1381
|
+
const paths2 = [];
|
|
1382
|
+
for (const [word, child] of Object.entries(node)) {
|
|
1383
|
+
const path = [...prefix, word];
|
|
1384
|
+
paths2.push(path);
|
|
1385
|
+
paths2.push(...surfacePaths(child, path));
|
|
1386
|
+
}
|
|
1387
|
+
return paths2;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1191
1390
|
// src/admin-spend.ts
|
|
1192
1391
|
async function call(ctx, method, scope) {
|
|
1193
1392
|
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
@@ -1237,9 +1436,7 @@ async function spendReset(ctx, scope) {
|
|
|
1237
1436
|
async function adminSpend(parsed, ctx) {
|
|
1238
1437
|
const action2 = parsed.positionals[2];
|
|
1239
1438
|
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1240
|
-
if (action2 !== "show" && action2 !== "reset")
|
|
1241
|
-
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1242
|
-
}
|
|
1439
|
+
if (action2 !== "show" && action2 !== "reset") rejectWord(["admin", "spend"], action2);
|
|
1243
1440
|
if (!scope) {
|
|
1244
1441
|
throw new Error(
|
|
1245
1442
|
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
@@ -2038,6 +2235,11 @@ var SET_OPTIONS = [
|
|
|
2038
2235
|
async function adminCommand(parsed, deps = {}) {
|
|
2039
2236
|
const area = parsed.positionals[1];
|
|
2040
2237
|
const action2 = parsed.positionals[2];
|
|
2238
|
+
if (area !== "ai" && area !== "spend") rejectWord(["admin"], area);
|
|
2239
|
+
if (!acceptedAfter(["admin", area]).includes(action2 ?? "")) rejectWord(["admin", area], action2);
|
|
2240
|
+
if (action2 === "credential" && !acceptedAfter(["admin", "ai", "credential"]).includes(parsed.positionals[3] ?? "")) {
|
|
2241
|
+
rejectWord(["admin", "ai", "credential"], parsed.positionals[3]);
|
|
2242
|
+
}
|
|
2041
2243
|
if (area === "spend") {
|
|
2042
2244
|
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
2043
2245
|
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2063,14 +2265,11 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2063
2265
|
out
|
|
2064
2266
|
});
|
|
2065
2267
|
}
|
|
2066
|
-
const credentialSet = action2 === "credential"
|
|
2268
|
+
const credentialSet = action2 === "credential";
|
|
2067
2269
|
const credentials = action2 === "credentials";
|
|
2068
2270
|
const models = action2 === "models";
|
|
2069
2271
|
const usage = action2 === "usage";
|
|
2070
2272
|
const audit = action2 === "audit";
|
|
2071
|
-
if (area !== "ai" || action2 !== "show" && action2 !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
2072
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
2073
|
-
}
|
|
2074
2273
|
const allowed = credentialSet ? [...CONTEXT_OPTIONS, "from-env", "stdin"] : action2 === "set" ? SET_OPTIONS : models ? [...JSON_OPTIONS, "provider"] : usage ? [...JSON_OPTIONS, "app-id", "env", "run-id", "limit"] : audit ? [...JSON_OPTIONS, "limit"] : JSON_OPTIONS;
|
|
2075
2274
|
assertArgs(parsed, allowed, credentialSet ? 5 : action2 === "set" ? 4 : 3);
|
|
2076
2275
|
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2284,9 +2483,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2284
2483
|
"json"
|
|
2285
2484
|
], 2);
|
|
2286
2485
|
const action2 = parsed.positionals[1] ?? "login";
|
|
2287
|
-
if (action2 !== "login")
|
|
2288
|
-
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
2289
|
-
}
|
|
2486
|
+
if (action2 !== "login") rejectWord(["auth"], action2);
|
|
2290
2487
|
const context = await resolveOperatorContext(parsed, {
|
|
2291
2488
|
allowMissingConfig: true,
|
|
2292
2489
|
requireApp: true
|
|
@@ -2359,9 +2556,7 @@ function bothTenants(cfg) {
|
|
|
2359
2556
|
// src/agent-command.ts
|
|
2360
2557
|
async function agentCommand(parsed, deps = {}) {
|
|
2361
2558
|
const action2 = parsed.positionals[1];
|
|
2362
|
-
if (action2 !== "jobs" && action2 !== "retry")
|
|
2363
|
-
throw new Error(`unknown agent action "${action2 ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
2364
|
-
}
|
|
2559
|
+
if (action2 !== "jobs" && action2 !== "retry") rejectWord(["agent"], action2);
|
|
2365
2560
|
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token"], action2 === "jobs" ? 2 : 3);
|
|
2366
2561
|
if (action2 === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
2367
2562
|
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
@@ -2474,8 +2669,8 @@ async function appImport(options) {
|
|
|
2474
2669
|
const out = options.stdout ?? console;
|
|
2475
2670
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2476
2671
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2477
|
-
const
|
|
2478
|
-
const { format, sources } = parseImport(
|
|
2672
|
+
const text4 = options.file === "-" ? (options.readStdin ?? (() => readFileSync5(0, "utf8")))() : readFileSync5(options.file, "utf8");
|
|
2673
|
+
const { format, sources } = parseImport(text4, options.ns);
|
|
2479
2674
|
if (format === "namespace-map" && options.ns) {
|
|
2480
2675
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
2481
2676
|
}
|
|
@@ -2536,9 +2731,7 @@ async function appOwnersCommand(parsed, dependencies = {}) {
|
|
|
2536
2731
|
await (sub === "add" ? ownersAdd(email, options) : ownersRemove(email, options));
|
|
2537
2732
|
return;
|
|
2538
2733
|
}
|
|
2539
|
-
|
|
2540
|
-
`unknown app owners subcommand "${sub}". Try "odla-ai app owners list", "odla-ai app owners add <email>", or "odla-ai app owners remove <email>".`
|
|
2541
|
-
);
|
|
2734
|
+
rejectWord(["app", "owners"], sub);
|
|
2542
2735
|
}
|
|
2543
2736
|
|
|
2544
2737
|
// src/app-rename.ts
|
|
@@ -2644,8 +2837,10 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2644
2837
|
return;
|
|
2645
2838
|
}
|
|
2646
2839
|
if (sub !== "archive" && sub !== "restore") {
|
|
2647
|
-
|
|
2648
|
-
|
|
2840
|
+
rejectWord(
|
|
2841
|
+
["app"],
|
|
2842
|
+
sub,
|
|
2843
|
+
"Permanent deletion has no CLI: it requires a signed-in owner in Studio."
|
|
2649
2844
|
);
|
|
2650
2845
|
}
|
|
2651
2846
|
assertArgs(parsed, ["config", "token", "email", "yes", "json"], 2);
|
|
@@ -2691,7 +2886,7 @@ var EXTENSIONS = {
|
|
|
2691
2886
|
"text/html": "html",
|
|
2692
2887
|
"application/json": "json"
|
|
2693
2888
|
};
|
|
2694
|
-
var encode = (
|
|
2889
|
+
var encode = (text4) => new TextEncoder().encode(text4);
|
|
2695
2890
|
function assetFileName(uuid, mime) {
|
|
2696
2891
|
const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
|
|
2697
2892
|
return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
|
|
@@ -2811,7 +3006,8 @@ async function brandCommand(parsed, deps) {
|
|
|
2811
3006
|
await designUnpack(parsed, deps);
|
|
2812
3007
|
return;
|
|
2813
3008
|
}
|
|
2814
|
-
|
|
3009
|
+
if (subject !== "design") rejectWord(["brand"], subject);
|
|
3010
|
+
rejectWord(["brand", "design"], action2, USAGE);
|
|
2815
3011
|
}
|
|
2816
3012
|
|
|
2817
3013
|
// src/calendar-errors.ts
|
|
@@ -3549,9 +3745,9 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
3549
3745
|
}
|
|
3550
3746
|
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
|
|
3551
3747
|
}
|
|
3552
|
-
function errorCode(
|
|
3748
|
+
function errorCode(text4) {
|
|
3553
3749
|
try {
|
|
3554
|
-
const body = JSON.parse(
|
|
3750
|
+
const body = JSON.parse(text4);
|
|
3555
3751
|
return typeof body.error?.code === "string" ? body.error.code : null;
|
|
3556
3752
|
} catch {
|
|
3557
3753
|
return null;
|
|
@@ -4286,15 +4482,15 @@ function readWranglerConfig(path) {
|
|
|
4286
4482
|
return null;
|
|
4287
4483
|
}
|
|
4288
4484
|
}
|
|
4289
|
-
function stripJsonComments(
|
|
4485
|
+
function stripJsonComments(text4) {
|
|
4290
4486
|
let result = "";
|
|
4291
4487
|
let inString = false;
|
|
4292
|
-
for (let i = 0; i <
|
|
4293
|
-
const ch =
|
|
4488
|
+
for (let i = 0; i < text4.length; i++) {
|
|
4489
|
+
const ch = text4[i];
|
|
4294
4490
|
if (inString) {
|
|
4295
4491
|
result += ch;
|
|
4296
4492
|
if (ch === "\\") {
|
|
4297
|
-
result +=
|
|
4493
|
+
result += text4[i + 1] ?? "";
|
|
4298
4494
|
i++;
|
|
4299
4495
|
} else if (ch === '"') {
|
|
4300
4496
|
inString = false;
|
|
@@ -4306,14 +4502,14 @@ function stripJsonComments(text3) {
|
|
|
4306
4502
|
result += ch;
|
|
4307
4503
|
continue;
|
|
4308
4504
|
}
|
|
4309
|
-
if (ch === "/" &&
|
|
4310
|
-
while (i <
|
|
4505
|
+
if (ch === "/" && text4[i + 1] === "/") {
|
|
4506
|
+
while (i < text4.length && text4[i] !== "\n") i++;
|
|
4311
4507
|
result += "\n";
|
|
4312
4508
|
continue;
|
|
4313
4509
|
}
|
|
4314
|
-
if (ch === "/" &&
|
|
4510
|
+
if (ch === "/" && text4[i + 1] === "*") {
|
|
4315
4511
|
i += 2;
|
|
4316
|
-
while (i <
|
|
4512
|
+
while (i < text4.length && !(text4[i] === "*" && text4[i + 1] === "/")) i++;
|
|
4317
4513
|
i++;
|
|
4318
4514
|
continue;
|
|
4319
4515
|
}
|
|
@@ -4827,9 +5023,9 @@ function initProject(options) {
|
|
|
4827
5023
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
4828
5024
|
out.log("updated .gitignore for local odla credentials");
|
|
4829
5025
|
}
|
|
4830
|
-
function writeIfMissing(path,
|
|
5026
|
+
function writeIfMissing(path, text4) {
|
|
4831
5027
|
if (existsSync9(path)) return;
|
|
4832
|
-
writeFileSync2(path,
|
|
5028
|
+
writeFileSync2(path, text4);
|
|
4833
5029
|
}
|
|
4834
5030
|
function configTemplate(input) {
|
|
4835
5031
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -5041,13 +5237,13 @@ async function secretsSetClerkKey(options) {
|
|
|
5041
5237
|
body: JSON.stringify({ value: value2 })
|
|
5042
5238
|
});
|
|
5043
5239
|
if (!res.ok) {
|
|
5044
|
-
const
|
|
5045
|
-
throw new Error(`store Clerk secret key failed (${res.status}): ${
|
|
5240
|
+
const text4 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
|
|
5241
|
+
throw new Error(`store Clerk secret key failed (${res.status}): ${text4 || "request failed"}`);
|
|
5046
5242
|
}
|
|
5047
5243
|
out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
|
|
5048
5244
|
}
|
|
5049
|
-
function scrubValue(
|
|
5050
|
-
return redactSecrets(
|
|
5245
|
+
function scrubValue(text4, value2) {
|
|
5246
|
+
return redactSecrets(text4).split(value2).join("[value redacted]");
|
|
5051
5247
|
}
|
|
5052
5248
|
async function resolveVaultWrite(options) {
|
|
5053
5249
|
const out = options.stdout ?? console;
|
|
@@ -5339,8 +5535,8 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5339
5535
|
harnesses: installations
|
|
5340
5536
|
};
|
|
5341
5537
|
}
|
|
5342
|
-
function pathsUnder(root,
|
|
5343
|
-
return [...
|
|
5538
|
+
function pathsUnder(root, paths2) {
|
|
5539
|
+
return [...paths2].map((path) => relative2(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute3(path)).sort();
|
|
5344
5540
|
}
|
|
5345
5541
|
function normalizeHarnesses(values, global) {
|
|
5346
5542
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5620,9 +5816,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5620
5816
|
return;
|
|
5621
5817
|
}
|
|
5622
5818
|
if (sub !== "push") {
|
|
5623
|
-
|
|
5624
|
-
`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".`
|
|
5625
|
-
);
|
|
5819
|
+
rejectWord(["secrets"], sub);
|
|
5626
5820
|
}
|
|
5627
5821
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
5628
5822
|
await secretsPush({
|
|
@@ -5635,7 +5829,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5635
5829
|
async function projectCommand(command, parsed, deps) {
|
|
5636
5830
|
if (command === "ai") {
|
|
5637
5831
|
const sub = parsed.positionals[1];
|
|
5638
|
-
if (sub !== "models")
|
|
5832
|
+
if (sub !== "models") rejectWord(["ai"], sub);
|
|
5639
5833
|
assertArgs(parsed, ["config", "env", "provider", "json"], 2);
|
|
5640
5834
|
await aiModels({
|
|
5641
5835
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -5649,9 +5843,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5649
5843
|
}
|
|
5650
5844
|
if (command === "config") {
|
|
5651
5845
|
const sub = parsed.positionals[1];
|
|
5652
|
-
if (sub !== "diff" && sub !== "plan" && sub !== "apply")
|
|
5653
|
-
throw new Error(`unknown config subcommand "${sub ?? ""}". Try "odla-ai config diff --json".`);
|
|
5654
|
-
}
|
|
5846
|
+
if (sub !== "diff" && sub !== "plan" && sub !== "apply") rejectWord(["config"], sub);
|
|
5655
5847
|
assertArgs(
|
|
5656
5848
|
parsed,
|
|
5657
5849
|
sub === "apply" ? ["config", "plan", "idempotency-key", "token", "email", "open", "json"] : ["config", "token", "email", "open", "json"],
|
|
@@ -5679,7 +5871,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5679
5871
|
if (command === "operations") {
|
|
5680
5872
|
const sub = parsed.positionals[1];
|
|
5681
5873
|
if (sub !== "get" && sub !== "wait") {
|
|
5682
|
-
|
|
5874
|
+
rejectWord(["operations"], sub);
|
|
5683
5875
|
}
|
|
5684
5876
|
assertArgs(
|
|
5685
5877
|
parsed,
|
|
@@ -5753,7 +5945,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5753
5945
|
}
|
|
5754
5946
|
if (command === "skill") {
|
|
5755
5947
|
const sub = parsed.positionals[1];
|
|
5756
|
-
if (sub !== "install")
|
|
5948
|
+
if (sub !== "install") rejectWord(["skill"], sub);
|
|
5757
5949
|
install(parsed, 2, deps);
|
|
5758
5950
|
return true;
|
|
5759
5951
|
}
|
|
@@ -5769,10 +5961,10 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
5769
5961
|
import { cpus, hostname, totalmem } from "os";
|
|
5770
5962
|
import { resolve as resolve11 } from "path";
|
|
5771
5963
|
|
|
5772
|
-
// ../harness/dist/chunk-
|
|
5964
|
+
// ../harness/dist/chunk-RXNHCGWE.js
|
|
5773
5965
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
5774
5966
|
|
|
5775
|
-
// ../harness/dist/chunk-
|
|
5967
|
+
// ../harness/dist/chunk-CR6RE3A2.js
|
|
5776
5968
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5777
5969
|
import { constants } from "fs";
|
|
5778
5970
|
import { access } from "fs/promises";
|
|
@@ -6024,12 +6216,12 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
6024
6216
|
});
|
|
6025
6217
|
if (outputBytes > 8 * 1024 * 1024) throw new Error("git file inventory exceeds 8 MiB");
|
|
6026
6218
|
if (code !== 0) throw new Error(`git file inventory failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6027
|
-
const
|
|
6028
|
-
if (
|
|
6219
|
+
const paths2 = Buffer.concat(stdout).toString("utf8").split("\0").filter(Boolean).sort();
|
|
6220
|
+
if (paths2.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
6029
6221
|
const root = resolve22(sourceDir);
|
|
6030
6222
|
const files = [];
|
|
6031
6223
|
let bytes = 0;
|
|
6032
|
-
for (const relativePath of
|
|
6224
|
+
for (const relativePath of paths2) {
|
|
6033
6225
|
if (!allowedWorkspacePath(relativePath)) continue;
|
|
6034
6226
|
const source = resolve22(root, relativePath);
|
|
6035
6227
|
if (!source.startsWith(`${root}${sep22}`)) throw new TypeError("git file path escapes workspace");
|
|
@@ -6145,7 +6337,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
6145
6337
|
}
|
|
6146
6338
|
}
|
|
6147
6339
|
|
|
6148
|
-
// ../harness/dist/chunk-
|
|
6340
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6149
6341
|
import { createHash as createHash3 } from "crypto";
|
|
6150
6342
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
6151
6343
|
import { relative as relative4, resolve as resolve10 } from "path";
|
|
@@ -6468,13 +6660,13 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6468
6660
|
if (!Number.isSafeInteger(limits.maximumFiles) || limits.maximumFiles < 1 || !Number.isSafeInteger(limits.maximumBytes) || limits.maximumBytes < 1 || snapshot.files.length > limits.maximumFiles) {
|
|
6469
6661
|
throw new CamelError("limit_exceeded", "Code snapshot exceeds its registered limits.");
|
|
6470
6662
|
}
|
|
6471
|
-
const
|
|
6663
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
6472
6664
|
let bytes = 0;
|
|
6473
6665
|
for (const file of snapshot.files) {
|
|
6474
|
-
if (!file.path || file.path.startsWith("/") || file.path.includes("\\") || file.path.split("/").some((part) => !part || part === "." || part === "..") ||
|
|
6666
|
+
if (!file.path || file.path.startsWith("/") || file.path.includes("\\") || file.path.split("/").some((part) => !part || part === "." || part === "..") || paths2.has(file.path) || typeof file.content !== "string") {
|
|
6475
6667
|
throw new CamelError("state_conflict", "Code snapshot contains an invalid or duplicate path.");
|
|
6476
6668
|
}
|
|
6477
|
-
|
|
6669
|
+
paths2.add(file.path);
|
|
6478
6670
|
bytes += utf8Length(file.path) + utf8Length(file.content);
|
|
6479
6671
|
}
|
|
6480
6672
|
if (bytes > limits.maximumBytes) {
|
|
@@ -6482,7 +6674,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6482
6674
|
}
|
|
6483
6675
|
}
|
|
6484
6676
|
|
|
6485
|
-
// ../harness/dist/chunk-
|
|
6677
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6486
6678
|
import { spawn as spawn4 } from "child_process";
|
|
6487
6679
|
import { lstat as lstat2 } from "fs/promises";
|
|
6488
6680
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -6628,8 +6820,8 @@ function boundedInteger(value2, spec) {
|
|
|
6628
6820
|
}
|
|
6629
6821
|
function boundedNumber(value2, spec) {
|
|
6630
6822
|
if (spec.kind !== "finite_number" || typeof value2 !== "number" || !Number.isFinite(value2) || value2 < spec.minimum || value2 > spec.maximum) throw new CamelError("conversion_rejected", "Finite-number conversion rejected the structured value.");
|
|
6631
|
-
const
|
|
6632
|
-
if (/e/i.test(
|
|
6823
|
+
const text4 = String(value2);
|
|
6824
|
+
if (/e/i.test(text4) || (text4.split(".")[1]?.length ?? 0) > spec.maximumDecimalPlaces) throw new CamelError("conversion_rejected", "Finite-number conversion rejected a non-canonical decimal.");
|
|
6633
6825
|
return value2;
|
|
6634
6826
|
}
|
|
6635
6827
|
function enumMember(value2, spec) {
|
|
@@ -6785,11 +6977,11 @@ function validateUnsafeSelector(path, value2, tool) {
|
|
|
6785
6977
|
return void 0;
|
|
6786
6978
|
}
|
|
6787
6979
|
function looksLikeDestination(value2) {
|
|
6788
|
-
const
|
|
6789
|
-
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(
|
|
6980
|
+
const text4 = value2.trim();
|
|
6981
|
+
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text4) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text4);
|
|
6790
6982
|
}
|
|
6791
6983
|
|
|
6792
|
-
// ../harness/dist/chunk-
|
|
6984
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6793
6985
|
import { readFile as readFile4, stat as stat2 } from "fs/promises";
|
|
6794
6986
|
import { readFile as readFile3 } from "fs/promises";
|
|
6795
6987
|
import { join as join33 } from "path";
|
|
@@ -6954,9 +7146,9 @@ async function extractImports(builder, input) {
|
|
|
6954
7146
|
const sources = input.paths.filter(isSourcePath);
|
|
6955
7147
|
const known = new Set(sources);
|
|
6956
7148
|
for (const path of sources) {
|
|
6957
|
-
let
|
|
7149
|
+
let text4;
|
|
6958
7150
|
try {
|
|
6959
|
-
|
|
7151
|
+
text4 = await input.read(path);
|
|
6960
7152
|
} catch {
|
|
6961
7153
|
continue;
|
|
6962
7154
|
}
|
|
@@ -6964,13 +7156,13 @@ async function extractImports(builder, input) {
|
|
|
6964
7156
|
const file = builder.node(FILE, path, pkg ? { pkg } : void 0);
|
|
6965
7157
|
if (pkg) builder.edge(builder.node(PACKAGE, pkg), CONTAINS, file);
|
|
6966
7158
|
const specifiers = /* @__PURE__ */ new Set();
|
|
6967
|
-
for (const match of
|
|
6968
|
-
for (const match of
|
|
7159
|
+
for (const match of text4.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
|
|
7160
|
+
for (const match of text4.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
|
|
6969
7161
|
for (const specifier of specifiers) {
|
|
6970
7162
|
const resolved = resolveImport(path, specifier, known);
|
|
6971
7163
|
if (resolved) builder.edge(file, IMPORTS, nodeId(FILE, resolved));
|
|
6972
7164
|
}
|
|
6973
|
-
for (const name of exportedNames(
|
|
7165
|
+
for (const name of exportedNames(text4)) {
|
|
6974
7166
|
builder.edge(file, EXPORTS, builder.node(SYMBOL, name));
|
|
6975
7167
|
}
|
|
6976
7168
|
}
|
|
@@ -7011,16 +7203,16 @@ async function extractData(builder, input) {
|
|
|
7011
7203
|
};
|
|
7012
7204
|
for (const path of input.paths) {
|
|
7013
7205
|
if (!SOURCE_FILE.test(path) || input.ignore?.(path)) continue;
|
|
7014
|
-
let
|
|
7206
|
+
let text4;
|
|
7015
7207
|
try {
|
|
7016
|
-
|
|
7208
|
+
text4 = await input.read(path);
|
|
7017
7209
|
} catch {
|
|
7018
7210
|
continue;
|
|
7019
7211
|
}
|
|
7020
|
-
for (const statement of
|
|
7212
|
+
for (const statement of text4.matchAll(STATEMENT)) {
|
|
7021
7213
|
const verb = statement[1].toUpperCase().replace(/\s+/g, " ");
|
|
7022
7214
|
const start = statement.index ?? 0;
|
|
7023
|
-
const rest =
|
|
7215
|
+
const rest = text4.slice(start + statement[0].length, start + STATEMENT_WINDOW);
|
|
7024
7216
|
if (verb === "SELECT") {
|
|
7025
7217
|
for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
|
|
7026
7218
|
continue;
|
|
@@ -7036,16 +7228,16 @@ async function extractData(builder, input) {
|
|
|
7036
7228
|
for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
|
|
7037
7229
|
}
|
|
7038
7230
|
}
|
|
7039
|
-
for (const match of
|
|
7040
|
-
touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(
|
|
7231
|
+
for (const match of text4.matchAll(NS_CONST)) {
|
|
7232
|
+
touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text4, match.index ?? 0));
|
|
7041
7233
|
}
|
|
7042
|
-
for (const match of
|
|
7043
|
-
touch(path, match[1], NAMESPACE, accessFor(
|
|
7234
|
+
for (const match of text4.matchAll(NS_LITERAL)) {
|
|
7235
|
+
touch(path, match[1], NAMESPACE, accessFor(text4, match.index ?? 0));
|
|
7044
7236
|
}
|
|
7045
7237
|
}
|
|
7046
7238
|
}
|
|
7047
|
-
function accessFor(
|
|
7048
|
-
const window =
|
|
7239
|
+
function accessFor(text4, index) {
|
|
7240
|
+
const window = text4.slice(Math.max(0, index - 160), index + 40);
|
|
7049
7241
|
return /\b(?:transact|update|delete|create|insert|Ops)\b/.test(window) ? WRITES : READS;
|
|
7050
7242
|
}
|
|
7051
7243
|
async function buildCodeGraph(input) {
|
|
@@ -7057,7 +7249,7 @@ async function buildCodeGraph(input) {
|
|
|
7057
7249
|
return builder.build();
|
|
7058
7250
|
}
|
|
7059
7251
|
|
|
7060
|
-
// ../harness/dist/chunk-
|
|
7252
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
7061
7253
|
import { createHash as createHash32 } from "crypto";
|
|
7062
7254
|
async function digestStagedWorkspace(root, limits) {
|
|
7063
7255
|
const files = [];
|
|
@@ -7385,7 +7577,7 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
7385
7577
|
if (FORBIDDEN.test(patch2) || /(?:old|new)(?: file)? mode 120000/.test(patch2)) {
|
|
7386
7578
|
throw new TypeError("patch uses a forbidden binary, link, mode, rename, or copy operation");
|
|
7387
7579
|
}
|
|
7388
|
-
const
|
|
7580
|
+
const paths2 = [];
|
|
7389
7581
|
const lines = patch2.split("\n");
|
|
7390
7582
|
for (let index = 0; index < lines.length; index += 1) {
|
|
7391
7583
|
const line2 = lines[index];
|
|
@@ -7401,10 +7593,10 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
7401
7593
|
if (!validHeaderPath(oldPath, path, "a") || !validHeaderPath(newPath, path, "b")) {
|
|
7402
7594
|
throw new TypeError("patch file headers do not match the declared path");
|
|
7403
7595
|
}
|
|
7404
|
-
|
|
7596
|
+
paths2.push(path);
|
|
7405
7597
|
}
|
|
7406
|
-
if (!
|
|
7407
|
-
return
|
|
7598
|
+
if (!paths2.length || new Set(paths2).size !== paths2.length) throw new TypeError("patch has no diffs or repeats a path");
|
|
7599
|
+
return paths2;
|
|
7408
7600
|
}
|
|
7409
7601
|
function validHeaderPath(value2, path, prefix) {
|
|
7410
7602
|
return value2 === "/dev/null" || value2 === `${prefix}/${path}`;
|
|
@@ -7429,11 +7621,11 @@ function describePatchFailure(patch2, detail) {
|
|
|
7429
7621
|
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
7430
7622
|
return `patch did not apply: ${detail}${hint}`;
|
|
7431
7623
|
}
|
|
7432
|
-
async function applyCodePatch(workspaceDir, rawPatch,
|
|
7624
|
+
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
7433
7625
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
7434
7626
|
await gitApply(workspaceDir, patch2, true);
|
|
7435
7627
|
await gitApply(workspaceDir, patch2, false);
|
|
7436
|
-
for (const path of
|
|
7628
|
+
for (const path of paths2) {
|
|
7437
7629
|
try {
|
|
7438
7630
|
const info = await lstat2(resolveCodePath(workspaceDir, path));
|
|
7439
7631
|
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
|
|
@@ -7455,8 +7647,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
7455
7647
|
});
|
|
7456
7648
|
let stderr2 = "";
|
|
7457
7649
|
child.stderr.setEncoding("utf8");
|
|
7458
|
-
child.stderr.on("data", (
|
|
7459
|
-
if (stderr2.length < 4e3) stderr2 +=
|
|
7650
|
+
child.stderr.on("data", (text22) => {
|
|
7651
|
+
if (stderr2.length < 4e3) stderr2 += text22.slice(0, 4e3);
|
|
7460
7652
|
});
|
|
7461
7653
|
child.once("error", reject);
|
|
7462
7654
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
|
|
@@ -7480,8 +7672,8 @@ async function restoreCodeWorkspaceCheckpoint(input) {
|
|
|
7480
7672
|
const workspace = await stageWorkspace(input.trustedBaseDir, input.stage);
|
|
7481
7673
|
try {
|
|
7482
7674
|
if (checkpoint.patch) {
|
|
7483
|
-
const
|
|
7484
|
-
await applyCodePatch(workspace.workspaceDir, checkpoint.patch,
|
|
7675
|
+
const paths2 = validateCodePatch(checkpoint.patch, 256 * 1024);
|
|
7676
|
+
await applyCodePatch(workspace.workspaceDir, checkpoint.patch, paths2);
|
|
7485
7677
|
}
|
|
7486
7678
|
return { workspace, checkpoint };
|
|
7487
7679
|
} catch (error) {
|
|
@@ -7630,8 +7822,8 @@ async function verifyCodeCandidate(input) {
|
|
|
7630
7822
|
try {
|
|
7631
7823
|
const baseDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
7632
7824
|
if (baseDigest !== input.trustedBaseDigest) throw new TypeError("trusted base does not match its registered digest");
|
|
7633
|
-
const
|
|
7634
|
-
await applyCodePatch(staged.workspaceDir, input.candidatePatch,
|
|
7825
|
+
const paths2 = validateCodePatch(input.candidatePatch, policy.maximumPatchBytes);
|
|
7826
|
+
await applyCodePatch(staged.workspaceDir, input.candidatePatch, paths2);
|
|
7635
7827
|
const sourceDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
7636
7828
|
const policyDigest = digestPolicy(policy);
|
|
7637
7829
|
const patchDigest = digestBytes(input.candidatePatch);
|
|
@@ -7640,7 +7832,7 @@ async function verifyCodeCandidate(input) {
|
|
|
7640
7832
|
trustedBaseDigest: input.trustedBaseDigest,
|
|
7641
7833
|
patchDigest
|
|
7642
7834
|
});
|
|
7643
|
-
const changedTests = changedTestPaths(
|
|
7835
|
+
const changedTests = changedTestPaths(paths2, policy);
|
|
7644
7836
|
if (changedTests.length > policy.maximumChangedTests) throw new TypeError("candidate changes too many test files");
|
|
7645
7837
|
const recipes = [];
|
|
7646
7838
|
const logs = [];
|
|
@@ -7707,8 +7899,8 @@ function validate2(input) {
|
|
|
7707
7899
|
}
|
|
7708
7900
|
return result;
|
|
7709
7901
|
}
|
|
7710
|
-
function changedTestPaths(
|
|
7711
|
-
return
|
|
7902
|
+
function changedTestPaths(paths2, policy) {
|
|
7903
|
+
return paths2.filter((path) => policy.testPathSuffixes.some((suffix) => path.endsWith(suffix)) || policy.testPathPrefixes.some((prefix) => path.startsWith(prefix) || path.includes(`/${prefix}`))).sort();
|
|
7712
7904
|
}
|
|
7713
7905
|
function recipeReceipt(recipe2, result, artifacts) {
|
|
7714
7906
|
const status = result.timedOut ? "timed_out" : result.outputLimitExceeded ? "output_limited" : result.exitCode === 0 && artifacts.every((item) => item.status === "verified") ? "passed" : "failed";
|
|
@@ -8439,7 +8631,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
8439
8631
|
files(root) {
|
|
8440
8632
|
const existing = cache2.get(root);
|
|
8441
8633
|
if (existing) return existing;
|
|
8442
|
-
const pending = enumerate(root, limit).then((
|
|
8634
|
+
const pending = enumerate(root, limit).then((paths2) => Object.freeze(paths2));
|
|
8443
8635
|
cache2.set(root, pending);
|
|
8444
8636
|
void pending.catch(() => {
|
|
8445
8637
|
if (cache2.get(root) === pending) cache2.delete(root);
|
|
@@ -8452,7 +8644,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
8452
8644
|
};
|
|
8453
8645
|
}
|
|
8454
8646
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
8455
|
-
const
|
|
8647
|
+
const paths2 = [];
|
|
8456
8648
|
const walk = async (directory) => {
|
|
8457
8649
|
for (const entry of await readdir22(directory, { withFileTypes: true })) {
|
|
8458
8650
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
@@ -8466,26 +8658,26 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
8466
8658
|
} catch {
|
|
8467
8659
|
continue;
|
|
8468
8660
|
}
|
|
8469
|
-
|
|
8470
|
-
if (
|
|
8661
|
+
paths2.push(path);
|
|
8662
|
+
if (paths2.length > limit) throw new TypeError("workspace file registry exceeds its bound");
|
|
8471
8663
|
}
|
|
8472
8664
|
}
|
|
8473
8665
|
};
|
|
8474
8666
|
await walk(resolve42(root));
|
|
8475
|
-
return
|
|
8667
|
+
return paths2.sort();
|
|
8476
8668
|
}
|
|
8477
|
-
function listWorkspace(
|
|
8669
|
+
function listWorkspace(paths2, options = {}) {
|
|
8478
8670
|
const max = options.maxEntries ?? 1e3;
|
|
8479
8671
|
const prefix = options.prefix?.replace(/\/+$/, "");
|
|
8480
|
-
const scoped = prefix ?
|
|
8672
|
+
const scoped = prefix ? paths2.filter((path) => path === prefix || path.startsWith(`${prefix}/`)) : [...paths2];
|
|
8481
8673
|
return scoped.slice(0, max);
|
|
8482
8674
|
}
|
|
8483
|
-
async function searchWorkspace(root,
|
|
8675
|
+
async function searchWorkspace(root, paths2, options) {
|
|
8484
8676
|
options.signal?.throwIfAborted();
|
|
8485
8677
|
if (!options.query) throw new TypeError("search query must be a non-empty string");
|
|
8486
8678
|
const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
8487
8679
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
8488
|
-
const scoped = listWorkspace(
|
|
8680
|
+
const scoped = listWorkspace(paths2, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths2.length });
|
|
8489
8681
|
if (scoped.length === 0) return [];
|
|
8490
8682
|
try {
|
|
8491
8683
|
return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
@@ -8495,11 +8687,11 @@ async function searchWorkspace(root, paths, options) {
|
|
|
8495
8687
|
}
|
|
8496
8688
|
}
|
|
8497
8689
|
var MAX_NATIVE_ARG_BYTES = 96 * 1024;
|
|
8498
|
-
async function nativeSearch(root,
|
|
8690
|
+
async function nativeSearch(root, paths2, options) {
|
|
8499
8691
|
const batches = [];
|
|
8500
8692
|
let batch = [];
|
|
8501
8693
|
let bytes = 0;
|
|
8502
|
-
for (const path of
|
|
8694
|
+
for (const path of paths2) {
|
|
8503
8695
|
const size = Buffer.byteLength(path) + 1;
|
|
8504
8696
|
if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
|
|
8505
8697
|
batches.push(batch);
|
|
@@ -8518,7 +8710,7 @@ async function nativeSearch(root, paths, options) {
|
|
|
8518
8710
|
}
|
|
8519
8711
|
return matches;
|
|
8520
8712
|
}
|
|
8521
|
-
function nativeSearchBatch(root,
|
|
8713
|
+
function nativeSearchBatch(root, paths2, options, remaining) {
|
|
8522
8714
|
return new Promise((resolveMatches, reject) => {
|
|
8523
8715
|
const args = [
|
|
8524
8716
|
"--fixed-strings",
|
|
@@ -8531,7 +8723,7 @@ function nativeSearchBatch(root, paths, options, remaining) {
|
|
|
8531
8723
|
options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
|
|
8532
8724
|
"--",
|
|
8533
8725
|
options.query,
|
|
8534
|
-
...
|
|
8726
|
+
...paths2
|
|
8535
8727
|
];
|
|
8536
8728
|
const child = spawn33("rg", args, {
|
|
8537
8729
|
cwd: root,
|
|
@@ -8717,16 +8909,16 @@ function createCodePolicyGate(options) {
|
|
|
8717
8909
|
}
|
|
8718
8910
|
};
|
|
8719
8911
|
}
|
|
8720
|
-
function directoryPrefixes(
|
|
8912
|
+
function directoryPrefixes(paths2) {
|
|
8721
8913
|
const prefixes = /* @__PURE__ */ new Set(["."]);
|
|
8722
|
-
for (const path of
|
|
8914
|
+
for (const path of paths2) {
|
|
8723
8915
|
const parts = path.split("/");
|
|
8724
8916
|
for (let index = 1; index < parts.length; index += 1) prefixes.add(parts.slice(0, index).join("/"));
|
|
8725
8917
|
}
|
|
8726
8918
|
return [...prefixes].sort();
|
|
8727
8919
|
}
|
|
8728
|
-
async function safePrefix(base,
|
|
8729
|
-
const prefixes = directoryPrefixes(
|
|
8920
|
+
async function safePrefix(base, paths2, prefix) {
|
|
8921
|
+
const prefixes = directoryPrefixes(paths2);
|
|
8730
8922
|
const conversions = await conversionRegistry(
|
|
8731
8923
|
[await registeredPolicy("code.prefix.v1", "code.prefixes.v1", prefixes)],
|
|
8732
8924
|
{ "code.prefixes.v1": prefixes }
|
|
@@ -8838,7 +9030,7 @@ function response(request3, ok, content2, details) {
|
|
|
8838
9030
|
return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
8839
9031
|
}
|
|
8840
9032
|
var cache = /* @__PURE__ */ new Map();
|
|
8841
|
-
function workspaceGraphs(workspaceDir,
|
|
9033
|
+
function workspaceGraphs(workspaceDir, paths2) {
|
|
8842
9034
|
const existing = cache.get(workspaceDir);
|
|
8843
9035
|
if (existing) return existing;
|
|
8844
9036
|
const read22 = (path) => readFile3(join33(workspaceDir, path), "utf8");
|
|
@@ -8846,7 +9038,7 @@ function workspaceGraphs(workspaceDir, paths) {
|
|
|
8846
9038
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
8847
9039
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
8848
9040
|
// with ground truth should build the graph themselves.
|
|
8849
|
-
graph: await buildCodeGraph({ paths, read: read22, data: { ignore: (path) => path.includes(".generated.") } })
|
|
9041
|
+
graph: await buildCodeGraph({ paths: paths2, read: read22, data: { ignore: (path) => path.includes(".generated.") } })
|
|
8850
9042
|
}))();
|
|
8851
9043
|
cache.set(workspaceDir, built);
|
|
8852
9044
|
return built;
|
|
@@ -8906,11 +9098,11 @@ async function read(context, request3, options, policy, registry) {
|
|
|
8906
9098
|
if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
|
|
8907
9099
|
throw new TypeError("requested line range exceeds its bound");
|
|
8908
9100
|
}
|
|
8909
|
-
const
|
|
8910
|
-
if (!
|
|
9101
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9102
|
+
if (!paths2.includes(path)) {
|
|
8911
9103
|
throw new TypeError(`no such file in the staged workspace: "${path}". Use sandbox.overview, sandbox.where_is or sandbox.search to find the correct path.`);
|
|
8912
9104
|
}
|
|
8913
|
-
const allowed = await policy.read(policyContext(context, request3, options, { paths, path, startLine, endLine }));
|
|
9105
|
+
const allowed = await policy.read(policyContext(context, request3, options, { paths: paths2, path, startLine, endLine }));
|
|
8914
9106
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
8915
9107
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
8916
9108
|
const info = await stat2(target);
|
|
@@ -8932,16 +9124,16 @@ async function list(context, request3, options, policy, registry) {
|
|
|
8932
9124
|
const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
8933
9125
|
const maxEntries = optionalInteger(request3.input.maxEntries) ?? 1e3;
|
|
8934
9126
|
if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
|
|
8935
|
-
const
|
|
8936
|
-
const allowed = await policy.list(policyContext(context, request3, options, { paths, ...prefix ? { prefix } : {} }));
|
|
9127
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9128
|
+
const allowed = await policy.list(policyContext(context, request3, options, { paths: paths2, ...prefix ? { prefix } : {} }));
|
|
8937
9129
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
8938
|
-
const entries = listWorkspace(
|
|
9130
|
+
const entries = listWorkspace(paths2, { ...prefix ? { prefix } : {}, maxEntries });
|
|
8939
9131
|
if (!entries.length) {
|
|
8940
9132
|
return response(request3, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
|
|
8941
9133
|
}
|
|
8942
|
-
const truncated = entries.length <
|
|
8943
|
-
const hint = !prefix &&
|
|
8944
|
-
\u2026 ${
|
|
9134
|
+
const truncated = entries.length < paths2.length && entries.length === maxEntries;
|
|
9135
|
+
const hint = !prefix && paths2.length > 500 ? `
|
|
9136
|
+
\u2026 ${paths2.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
|
|
8945
9137
|
return response(
|
|
8946
9138
|
request3,
|
|
8947
9139
|
true,
|
|
@@ -8959,10 +9151,10 @@ async function search(context, request3, options, policy, registry) {
|
|
|
8959
9151
|
const maxResults = optionalInteger(request3.input.maxResults) ?? 100;
|
|
8960
9152
|
if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
|
|
8961
9153
|
const caseSensitive = request3.input.caseSensitive === void 0 ? true : request3.input.caseSensitive === true;
|
|
8962
|
-
const
|
|
8963
|
-
const allowed = await policy.search(policyContext(context, request3, options, { paths, query, ...prefix ? { prefix } : {} }));
|
|
9154
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9155
|
+
const allowed = await policy.search(policyContext(context, request3, options, { paths: paths2, query, ...prefix ? { prefix } : {} }));
|
|
8964
9156
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
8965
|
-
const matches = await searchWorkspace(context.workspaceDir,
|
|
9157
|
+
const matches = await searchWorkspace(context.workspaceDir, paths2, {
|
|
8966
9158
|
query,
|
|
8967
9159
|
maxResults,
|
|
8968
9160
|
caseSensitive,
|
|
@@ -8984,8 +9176,8 @@ async function graphQuery(context, request3, options, policy, registry) {
|
|
|
8984
9176
|
selector: query
|
|
8985
9177
|
}));
|
|
8986
9178
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
8987
|
-
const
|
|
8988
|
-
const graphs = await workspaceGraphs(context.workspaceDir,
|
|
9179
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9180
|
+
const graphs = await workspaceGraphs(context.workspaceDir, paths2);
|
|
8989
9181
|
if (request3.tool === "sandbox.overview") {
|
|
8990
9182
|
return response(request3, true, renderOverview(graphs, query || void 0));
|
|
8991
9183
|
}
|
|
@@ -9047,16 +9239,16 @@ function toolFailureMessage(reason) {
|
|
|
9047
9239
|
async function patch(context, request3, options, policy, registry) {
|
|
9048
9240
|
exactKeys(request3.input, ["patch"]);
|
|
9049
9241
|
const value2 = stringField(request3.input, "patch");
|
|
9050
|
-
const
|
|
9051
|
-
if (
|
|
9242
|
+
const paths2 = validateCodePatch(value2, options.maxPatchBytes ?? 256 * 1024);
|
|
9243
|
+
if (paths2.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
9052
9244
|
throw new TypeError("patch targets a read-only reference source");
|
|
9053
9245
|
}
|
|
9054
9246
|
const allowed = await policy.patch(policyContext(context, request3, options, { patch: value2 }));
|
|
9055
9247
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9056
|
-
await applyCodePatch(context.workspaceDir, value2,
|
|
9248
|
+
await applyCodePatch(context.workspaceDir, value2, paths2);
|
|
9057
9249
|
registry.invalidate(context.workspaceDir);
|
|
9058
9250
|
forgetWorkspaceGraphs(context.workspaceDir);
|
|
9059
|
-
return response(request3, true, `Applied patch to ${
|
|
9251
|
+
return response(request3, true, `Applied patch to ${paths2.length} file(s).`, { paths: paths2 });
|
|
9060
9252
|
}
|
|
9061
9253
|
async function recipe(context, request3, options, recipes, policy) {
|
|
9062
9254
|
exactKeys(request3.input, ["recipeId"]);
|
|
@@ -9405,11 +9597,126 @@ async function startGoalPursuit(input) {
|
|
|
9405
9597
|
async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
9406
9598
|
const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
|
|
9407
9599
|
refs.push(eventId);
|
|
9408
|
-
const
|
|
9600
|
+
const attributed = { ...event, interactionId: command.commandId };
|
|
9601
|
+
const bounded = attributed.type === "message" ? { ...attributed, body: attributed.body.trim().slice(0, 2e4) || `${attributed.actor} event` } : attributed;
|
|
9409
9602
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
9410
9603
|
}
|
|
9411
9604
|
var digestRuntimeValue = (value2) => `sha256:${createHash32("sha256").update(value2).digest("hex")}`;
|
|
9412
9605
|
var runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
|
|
9606
|
+
var text3 = (value2, maximum) => {
|
|
9607
|
+
if (typeof value2 !== "string") return void 0;
|
|
9608
|
+
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
9609
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
9610
|
+
};
|
|
9611
|
+
var integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
9612
|
+
var record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
9613
|
+
var excerpt = (value2, tail = false) => {
|
|
9614
|
+
if (typeof value2 !== "string") return void 0;
|
|
9615
|
+
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
9616
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
9617
|
+
if (!source) return void 0;
|
|
9618
|
+
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
9619
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
9620
|
+
return text3(selected.join("\n"), 2400);
|
|
9621
|
+
};
|
|
9622
|
+
var paths = (value2) => {
|
|
9623
|
+
if (!Array.isArray(value2)) return void 0;
|
|
9624
|
+
const items = value2.flatMap((item) => {
|
|
9625
|
+
const path = text3(item, 1024);
|
|
9626
|
+
return path ? [path] : [];
|
|
9627
|
+
}).slice(0, 12);
|
|
9628
|
+
return items.length ? items : void 0;
|
|
9629
|
+
};
|
|
9630
|
+
function patchStats(value2) {
|
|
9631
|
+
if (typeof value2 !== "string") return {};
|
|
9632
|
+
let additions = 0;
|
|
9633
|
+
let deletions = 0;
|
|
9634
|
+
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
9635
|
+
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
9636
|
+
if (line2.startsWith("+")) additions += 1;
|
|
9637
|
+
else if (line2.startsWith("-")) deletions += 1;
|
|
9638
|
+
}
|
|
9639
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
9640
|
+
}
|
|
9641
|
+
function searchResults(value2) {
|
|
9642
|
+
if (typeof value2 !== "string") return void 0;
|
|
9643
|
+
const results = value2.split("\n").flatMap((line2) => {
|
|
9644
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
9645
|
+
if (!match) return [];
|
|
9646
|
+
const lineNumber = Number(match[2]);
|
|
9647
|
+
const itemText = text3(match[3], 240);
|
|
9648
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
9649
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
9650
|
+
}).slice(0, 5);
|
|
9651
|
+
return results.length ? results : void 0;
|
|
9652
|
+
}
|
|
9653
|
+
function codeToolRequestPresentation(request3) {
|
|
9654
|
+
const input = request3.input;
|
|
9655
|
+
if (request3.tool === "sandbox.read") {
|
|
9656
|
+
const path = text3(input.path, 1024);
|
|
9657
|
+
if (!path) return void 0;
|
|
9658
|
+
const startLine = integer2(input.startLine);
|
|
9659
|
+
const endLine = integer2(input.endLine);
|
|
9660
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
9661
|
+
}
|
|
9662
|
+
if (request3.tool === "sandbox.list") {
|
|
9663
|
+
const scope = text3(input.prefix, 1024);
|
|
9664
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
9665
|
+
}
|
|
9666
|
+
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
9667
|
+
const query = text3(input.query, 512);
|
|
9668
|
+
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
9669
|
+
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
9670
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
9671
|
+
}
|
|
9672
|
+
if (request3.tool === "sandbox.apply_patch") {
|
|
9673
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
9674
|
+
}
|
|
9675
|
+
const recipeId = text3(input.recipeId, 120);
|
|
9676
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
9677
|
+
}
|
|
9678
|
+
function codeToolResultPresentation(request3, response2) {
|
|
9679
|
+
const started = codeToolRequestPresentation(request3);
|
|
9680
|
+
if (!started || !response2.ok) return started;
|
|
9681
|
+
const details = record32(response2.details);
|
|
9682
|
+
if (started.kind === "read") {
|
|
9683
|
+
return {
|
|
9684
|
+
...started,
|
|
9685
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
9686
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
9687
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
9688
|
+
};
|
|
9689
|
+
}
|
|
9690
|
+
if (started.kind === "list") {
|
|
9691
|
+
const listed = response2.content.split("\n").filter((line2) => line2 && !line2.startsWith("\u2026") && !line2.startsWith("Workspace ")).map((line2) => text3(line2, 1024)).filter((line2) => Boolean(line2)).slice(0, 8);
|
|
9692
|
+
return {
|
|
9693
|
+
...started,
|
|
9694
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9695
|
+
...listed.length ? { paths: listed } : {}
|
|
9696
|
+
};
|
|
9697
|
+
}
|
|
9698
|
+
if (started.kind === "query") {
|
|
9699
|
+
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
9700
|
+
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
9701
|
+
return {
|
|
9702
|
+
...started,
|
|
9703
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9704
|
+
...results ? { results } : {},
|
|
9705
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
9706
|
+
};
|
|
9707
|
+
}
|
|
9708
|
+
if (started.kind === "patch") {
|
|
9709
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
9710
|
+
}
|
|
9711
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
9712
|
+
return {
|
|
9713
|
+
...started,
|
|
9714
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
9715
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
9716
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
9717
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
9718
|
+
};
|
|
9719
|
+
}
|
|
9413
9720
|
var TheseusRuntimeEngine = class {
|
|
9414
9721
|
constructor(options) {
|
|
9415
9722
|
this.options = options;
|
|
@@ -9643,18 +9950,29 @@ var TheseusRuntimeEngine = class {
|
|
|
9643
9950
|
return {
|
|
9644
9951
|
execute: async (context, request3) => {
|
|
9645
9952
|
const startedAt = Date.now();
|
|
9953
|
+
const operationId = digestRuntimeValue(`${command.commandId}:${request3.requestId}`);
|
|
9954
|
+
const startedPresentation = codeToolRequestPresentation(request3);
|
|
9646
9955
|
await this.#event(
|
|
9647
9956
|
command,
|
|
9648
|
-
{
|
|
9957
|
+
{
|
|
9958
|
+
type: "tool",
|
|
9959
|
+
phase: "started",
|
|
9960
|
+
tool: request3.tool,
|
|
9961
|
+
operationId,
|
|
9962
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
9963
|
+
},
|
|
9649
9964
|
active.conversationRefs
|
|
9650
9965
|
).catch(() => void 0);
|
|
9651
9966
|
const response2 = await broker.execute(context, request3);
|
|
9967
|
+
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
9652
9968
|
await this.#event(command, {
|
|
9653
9969
|
type: "tool",
|
|
9654
9970
|
phase: "completed",
|
|
9655
9971
|
tool: request3.tool,
|
|
9656
9972
|
ok: response2.ok,
|
|
9657
|
-
durationMs: Date.now() - startedAt
|
|
9973
|
+
durationMs: Date.now() - startedAt,
|
|
9974
|
+
operationId,
|
|
9975
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
9658
9976
|
}, active.conversationRefs).catch(() => void 0);
|
|
9659
9977
|
return response2;
|
|
9660
9978
|
}
|
|
@@ -10188,9 +10506,7 @@ function grantsUrl(cfg, suffix = "") {
|
|
|
10188
10506
|
async function codeGrantCommand(parsed, deps = {}) {
|
|
10189
10507
|
const action2 = parsed.positionals[2];
|
|
10190
10508
|
if (!isAction(action2)) {
|
|
10191
|
-
|
|
10192
|
-
`unknown code grant action "${action2 ?? ""}". Try "odla-ai code grant list --env dev".`
|
|
10193
|
-
);
|
|
10509
|
+
rejectWord(["code", "grant"], action2);
|
|
10194
10510
|
}
|
|
10195
10511
|
const decides = action2 === "approve" || action2 === "revoke";
|
|
10196
10512
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open"], decides ? 4 : 3);
|
|
@@ -10310,9 +10626,7 @@ function repositoryUrl(cfg, suffix = "") {
|
|
|
10310
10626
|
async function codeRepositoryCommand(parsed, deps = {}) {
|
|
10311
10627
|
const action2 = parsed.positionals[2];
|
|
10312
10628
|
if (!isAction2(action2)) {
|
|
10313
|
-
|
|
10314
|
-
`unknown code repository action "${action2 ?? ""}". Try "odla-ai code repository show --env dev".`
|
|
10315
|
-
);
|
|
10629
|
+
rejectWord(["code", "repository"], action2);
|
|
10316
10630
|
}
|
|
10317
10631
|
assertArgs(parsed, ["config", "env", "repo", "json", "token", "email", "open"], 3);
|
|
10318
10632
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
@@ -10387,9 +10701,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10387
10701
|
});
|
|
10388
10702
|
}
|
|
10389
10703
|
if (sub !== "connect") {
|
|
10390
|
-
|
|
10391
|
-
`unknown code subcommand "${sub ?? ""}". Try "odla-ai code connect --env dev" or "odla-ai code grant list --env dev".`
|
|
10392
|
-
);
|
|
10704
|
+
rejectWord(["code"], sub);
|
|
10393
10705
|
}
|
|
10394
10706
|
assertArgs(parsed, [
|
|
10395
10707
|
"config",
|
|
@@ -10526,9 +10838,7 @@ async function contextCommand(parsed, deps = {}) {
|
|
|
10526
10838
|
return;
|
|
10527
10839
|
}
|
|
10528
10840
|
if (action2 !== "show") {
|
|
10529
|
-
|
|
10530
|
-
`unknown context action "${action2 ?? ""}". Try show|list|save|remove.`
|
|
10531
|
-
);
|
|
10841
|
+
rejectWord(["context"], action2);
|
|
10532
10842
|
}
|
|
10533
10843
|
const context = await resolveOperatorContext(parsed, {
|
|
10534
10844
|
allowMissingConfig: true,
|
|
@@ -10584,9 +10894,7 @@ async function responseError(response2) {
|
|
|
10584
10894
|
}
|
|
10585
10895
|
async function credentialCommand(parsed, deps = {}) {
|
|
10586
10896
|
const action2 = parsed.positionals[1] ?? "list";
|
|
10587
|
-
if (action2 !== "list" && action2 !== "revoke")
|
|
10588
|
-
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
10589
|
-
}
|
|
10897
|
+
if (action2 !== "list" && action2 !== "revoke") rejectWord(["credentials"], action2);
|
|
10590
10898
|
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
10591
10899
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
10592
10900
|
const doFetch = deps.fetch ?? fetch;
|
|
@@ -11588,7 +11896,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11588
11896
|
assertArgs(parsed, ALLOWED, 3);
|
|
11589
11897
|
const action2 = parsed.positionals[1];
|
|
11590
11898
|
const id2 = parsed.positionals[2];
|
|
11591
|
-
if (!
|
|
11899
|
+
if (!acceptedAfter(["discuss"]).includes(action2 ?? "")) rejectWord(["discuss"], action2);
|
|
11592
11900
|
const ctx = await buildContext(parsed, deps);
|
|
11593
11901
|
switch (action2) {
|
|
11594
11902
|
case "groups":
|
|
@@ -11612,7 +11920,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11612
11920
|
return;
|
|
11613
11921
|
}
|
|
11614
11922
|
default:
|
|
11615
|
-
|
|
11923
|
+
rejectWord(["discuss"], action2);
|
|
11616
11924
|
}
|
|
11617
11925
|
}
|
|
11618
11926
|
|
|
@@ -11688,8 +11996,8 @@ function collectFields(parsed, allowClear) {
|
|
|
11688
11996
|
if (allowClear) out[spec.key] = null;
|
|
11689
11997
|
continue;
|
|
11690
11998
|
}
|
|
11691
|
-
const
|
|
11692
|
-
out[spec.key] = spec.num ? Number(
|
|
11999
|
+
const text4 = stringOpt(value2);
|
|
12000
|
+
out[spec.key] = spec.num ? Number(text4) : text4;
|
|
11693
12001
|
}
|
|
11694
12002
|
return out;
|
|
11695
12003
|
}
|
|
@@ -12522,7 +12830,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12522
12830
|
assertArgs(parsed, COMMON_OPTIONS, 4);
|
|
12523
12831
|
return pmProjectUse(await buildContext2(parsed, deps), requireId2(parsed.positionals[3], action3));
|
|
12524
12832
|
}
|
|
12525
|
-
|
|
12833
|
+
rejectWord(["pm", "project"], action3);
|
|
12526
12834
|
}
|
|
12527
12835
|
if (word === "next") {
|
|
12528
12836
|
assertArgs(parsed, [...COMMON_OPTIONS, "app", "project", "verbose"], 2);
|
|
@@ -12553,10 +12861,10 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12553
12861
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
12554
12862
|
}
|
|
12555
12863
|
const entity = ALIASES[word];
|
|
12556
|
-
if (!entity)
|
|
12864
|
+
if (!entity) rejectWord(["pm"], word);
|
|
12557
12865
|
const requestedAction = parsed.positionals[2] ?? "list";
|
|
12558
12866
|
const action2 = canonicalAction(requestedAction);
|
|
12559
|
-
if (!action2)
|
|
12867
|
+
if (!action2) rejectWord(["pm", word], requestedAction);
|
|
12560
12868
|
assertArgs(parsed, allowedOptions(entity, action2), 4);
|
|
12561
12869
|
if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
|
|
12562
12870
|
throw new Error(`pm ${action2} is only valid for tasks`);
|
|
@@ -12642,12 +12950,7 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
12642
12950
|
if (action2 === "status") {
|
|
12643
12951
|
return platformStatus(parsed, deps);
|
|
12644
12952
|
}
|
|
12645
|
-
|
|
12646
|
-
`unknown platform action "${[
|
|
12647
|
-
action2,
|
|
12648
|
-
parsed.positionals[2]
|
|
12649
|
-
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
12650
|
-
);
|
|
12953
|
+
rejectWord(["platform"], action2);
|
|
12651
12954
|
}
|
|
12652
12955
|
async function platformStatus(parsed, deps) {
|
|
12653
12956
|
assertArgs(
|
|
@@ -12984,9 +13287,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
12984
13287
|
);
|
|
12985
13288
|
const action2 = parsed.positionals[1];
|
|
12986
13289
|
if (action2 !== "status") {
|
|
12987
|
-
|
|
12988
|
-
`unknown o11y action "${action2 ?? ""}". Try "odla-ai o11y status --json".`
|
|
12989
|
-
);
|
|
13290
|
+
rejectWord(["o11y"], action2);
|
|
12990
13291
|
}
|
|
12991
13292
|
const minutes = statusMinutes(
|
|
12992
13293
|
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
@@ -13101,14 +13402,14 @@ function statusMinutes(value2) {
|
|
|
13101
13402
|
}
|
|
13102
13403
|
async function read2(url, headers, doFetch) {
|
|
13103
13404
|
const response2 = await doFetch(url, { headers });
|
|
13104
|
-
const
|
|
13405
|
+
const text4 = await response2.text();
|
|
13105
13406
|
let body = {};
|
|
13106
|
-
if (
|
|
13407
|
+
if (text4) {
|
|
13107
13408
|
try {
|
|
13108
|
-
const value2 = JSON.parse(
|
|
13409
|
+
const value2 = JSON.parse(text4);
|
|
13109
13410
|
body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
|
|
13110
13411
|
} catch {
|
|
13111
|
-
body = { message:
|
|
13412
|
+
body = { message: text4.slice(0, 300) };
|
|
13112
13413
|
}
|
|
13113
13414
|
}
|
|
13114
13415
|
return { httpStatus: response2.status, body };
|
|
@@ -13239,9 +13540,7 @@ var OPTIONS = [
|
|
|
13239
13540
|
async function monitorCommand(parsed, deps = {}) {
|
|
13240
13541
|
assertArgs(parsed, OPTIONS, 3);
|
|
13241
13542
|
const action2 = parsed.positionals[1] ?? "status";
|
|
13242
|
-
if (!["
|
|
13243
|
-
throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
|
|
13244
|
-
}
|
|
13543
|
+
if (!acceptedAfter(["monitor"]).includes(action2)) rejectWord(["monitor"], action2);
|
|
13245
13544
|
const context = await resolveOperatorContext(parsed, {
|
|
13246
13545
|
allowMissingConfig: action2 !== "plan" && action2 !== "apply",
|
|
13247
13546
|
requireApp: true
|
|
@@ -13338,13 +13637,13 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
13338
13637
|
}
|
|
13339
13638
|
async function request2(url, init, doFetch) {
|
|
13340
13639
|
const response2 = await doFetch(url, init);
|
|
13341
|
-
const
|
|
13640
|
+
const text4 = await response2.text();
|
|
13342
13641
|
let body = {};
|
|
13343
13642
|
try {
|
|
13344
|
-
const parsed =
|
|
13643
|
+
const parsed = text4 ? JSON.parse(text4) : {};
|
|
13345
13644
|
body = record10(parsed) ? parsed : { value: parsed };
|
|
13346
13645
|
} catch {
|
|
13347
|
-
body = { message:
|
|
13646
|
+
body = { message: text4.slice(0, 500) };
|
|
13348
13647
|
}
|
|
13349
13648
|
if (!response2.ok) {
|
|
13350
13649
|
const error = record10(body.error) ? body.error : body;
|
|
@@ -13547,8 +13846,8 @@ function runtimeUrl(cfg, suffix = "") {
|
|
|
13547
13846
|
return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
|
|
13548
13847
|
}
|
|
13549
13848
|
async function safeError(response2) {
|
|
13550
|
-
const
|
|
13551
|
-
return redactSecrets(
|
|
13849
|
+
const text4 = await response2.text();
|
|
13850
|
+
return redactSecrets(text4.slice(0, 1e3));
|
|
13552
13851
|
}
|
|
13553
13852
|
async function finish(doFetch, cfg, token, sessionId, method) {
|
|
13554
13853
|
return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
|
|
@@ -13871,186 +14170,6 @@ async function provision(options) {
|
|
|
13871
14170
|
// src/record.ts
|
|
13872
14171
|
import { appendFileSync } from "fs";
|
|
13873
14172
|
import process19 from "process";
|
|
13874
|
-
|
|
13875
|
-
// src/surface.ts
|
|
13876
|
-
var PM_ACTIONS = {
|
|
13877
|
-
list: {},
|
|
13878
|
-
add: {},
|
|
13879
|
-
create: {},
|
|
13880
|
-
get: {},
|
|
13881
|
-
set: {},
|
|
13882
|
-
update: {},
|
|
13883
|
-
status: {},
|
|
13884
|
-
move: {},
|
|
13885
|
-
done: {},
|
|
13886
|
-
comment: {},
|
|
13887
|
-
comments: {},
|
|
13888
|
-
ref: {},
|
|
13889
|
-
rm: {},
|
|
13890
|
-
delete: {}
|
|
13891
|
-
};
|
|
13892
|
-
var PM_TASK_ACTIONS = {
|
|
13893
|
-
...PM_ACTIONS,
|
|
13894
|
-
ready: {},
|
|
13895
|
-
claim: {},
|
|
13896
|
-
release: {}
|
|
13897
|
-
};
|
|
13898
|
-
var PM_ENTITIES = {
|
|
13899
|
-
...Object.fromEntries(
|
|
13900
|
-
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
13901
|
-
),
|
|
13902
|
-
task: PM_TASK_ACTIONS,
|
|
13903
|
-
kanban: PM_TASK_ACTIONS
|
|
13904
|
-
};
|
|
13905
|
-
var COMMAND_SURFACE = {
|
|
13906
|
-
agent: { jobs: {}, retry: {} },
|
|
13907
|
-
ai: { models: {} },
|
|
13908
|
-
admin: {
|
|
13909
|
-
ai: {
|
|
13910
|
-
show: {},
|
|
13911
|
-
set: {},
|
|
13912
|
-
credentials: {},
|
|
13913
|
-
models: {},
|
|
13914
|
-
usage: {},
|
|
13915
|
-
audit: {},
|
|
13916
|
-
credential: { set: {} }
|
|
13917
|
-
}
|
|
13918
|
-
},
|
|
13919
|
-
app: {
|
|
13920
|
-
archive: {},
|
|
13921
|
-
restore: {},
|
|
13922
|
-
export: {},
|
|
13923
|
-
import: {},
|
|
13924
|
-
rename: {},
|
|
13925
|
-
"refresh-sandbox": {},
|
|
13926
|
-
"go-live": {},
|
|
13927
|
-
promote: {},
|
|
13928
|
-
owners: { list: {}, add: {}, remove: {} }
|
|
13929
|
-
},
|
|
13930
|
-
auth: { login: {} },
|
|
13931
|
-
brand: { design: { unpack: {} } },
|
|
13932
|
-
bug: { create: {}, list: {}, report: {} },
|
|
13933
|
-
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
13934
|
-
capabilities: {},
|
|
13935
|
-
code: {
|
|
13936
|
-
connect: {},
|
|
13937
|
-
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
13938
|
-
repository: { show: {}, list: {}, bind: {} }
|
|
13939
|
-
},
|
|
13940
|
-
config: { diff: {}, plan: {}, apply: {} },
|
|
13941
|
-
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
13942
|
-
credentials: { list: {}, revoke: {} },
|
|
13943
|
-
device: { enroll: {}, list: {}, revoke: {} },
|
|
13944
|
-
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
13945
|
-
discuss: {
|
|
13946
|
-
groups: {},
|
|
13947
|
-
list: {},
|
|
13948
|
-
topics: {},
|
|
13949
|
-
read: {},
|
|
13950
|
-
post: {},
|
|
13951
|
-
reply: {},
|
|
13952
|
-
resolve: {},
|
|
13953
|
-
who: {},
|
|
13954
|
-
watch: {}
|
|
13955
|
-
},
|
|
13956
|
-
doctor: {},
|
|
13957
|
-
help: {},
|
|
13958
|
-
init: {},
|
|
13959
|
-
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
13960
|
-
o11y: { status: {} },
|
|
13961
|
-
operations: { get: {}, wait: {} },
|
|
13962
|
-
platform: {
|
|
13963
|
-
status: {}
|
|
13964
|
-
},
|
|
13965
|
-
pm: {
|
|
13966
|
-
...PM_ENTITIES,
|
|
13967
|
-
project: { list: {}, add: {}, create: {}, use: {} },
|
|
13968
|
-
handoff: {},
|
|
13969
|
-
next: {},
|
|
13970
|
-
watch: {}
|
|
13971
|
-
},
|
|
13972
|
-
provision: {},
|
|
13973
|
-
runbook: {
|
|
13974
|
-
ask: {},
|
|
13975
|
-
search: {},
|
|
13976
|
-
impact: {},
|
|
13977
|
-
list: {},
|
|
13978
|
-
get: {},
|
|
13979
|
-
cat: {},
|
|
13980
|
-
new: {},
|
|
13981
|
-
edit: {},
|
|
13982
|
-
comment: {},
|
|
13983
|
-
import: {},
|
|
13984
|
-
visibility: {},
|
|
13985
|
-
publish: {},
|
|
13986
|
-
archive: {},
|
|
13987
|
-
history: {},
|
|
13988
|
-
revert: {},
|
|
13989
|
-
rm: {},
|
|
13990
|
-
lint: {}
|
|
13991
|
-
},
|
|
13992
|
-
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
13993
|
-
security: {
|
|
13994
|
-
plan: {},
|
|
13995
|
-
sources: {},
|
|
13996
|
-
run: {},
|
|
13997
|
-
status: {},
|
|
13998
|
-
report: {},
|
|
13999
|
-
github: { connect: {}, disconnect: {} }
|
|
14000
|
-
},
|
|
14001
|
-
setup: {},
|
|
14002
|
-
skill: { install: {} },
|
|
14003
|
-
smoke: {},
|
|
14004
|
-
version: {},
|
|
14005
|
-
whoami: {}
|
|
14006
|
-
};
|
|
14007
|
-
function acceptedAfter(path) {
|
|
14008
|
-
let node = COMMAND_SURFACE;
|
|
14009
|
-
for (const word of path) {
|
|
14010
|
-
node = node?.[word];
|
|
14011
|
-
if (!node) return [];
|
|
14012
|
-
}
|
|
14013
|
-
return Object.keys(node).sort();
|
|
14014
|
-
}
|
|
14015
|
-
function validateInvocation(words2) {
|
|
14016
|
-
let node = COMMAND_SURFACE;
|
|
14017
|
-
const walked = [];
|
|
14018
|
-
for (const word of words2) {
|
|
14019
|
-
if (Object.keys(node).length === 0) return null;
|
|
14020
|
-
const next = node[word];
|
|
14021
|
-
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
14022
|
-
walked.push(word);
|
|
14023
|
-
node = next;
|
|
14024
|
-
}
|
|
14025
|
-
return null;
|
|
14026
|
-
}
|
|
14027
|
-
function describeProblem(problem) {
|
|
14028
|
-
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
14029
|
-
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
14030
|
-
}
|
|
14031
|
-
function invocationPath(words2) {
|
|
14032
|
-
let node = COMMAND_SURFACE;
|
|
14033
|
-
const path = [];
|
|
14034
|
-
for (const word of words2) {
|
|
14035
|
-
const next = node[word];
|
|
14036
|
-
if (!next) break;
|
|
14037
|
-
path.push(word);
|
|
14038
|
-
node = next;
|
|
14039
|
-
if (Object.keys(node).length === 0) break;
|
|
14040
|
-
}
|
|
14041
|
-
return path;
|
|
14042
|
-
}
|
|
14043
|
-
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
14044
|
-
const paths = [];
|
|
14045
|
-
for (const [word, child] of Object.entries(node)) {
|
|
14046
|
-
const path = [...prefix, word];
|
|
14047
|
-
paths.push(path);
|
|
14048
|
-
paths.push(...surfacePaths(child, path));
|
|
14049
|
-
}
|
|
14050
|
-
return paths;
|
|
14051
|
-
}
|
|
14052
|
-
|
|
14053
|
-
// src/record.ts
|
|
14054
14173
|
function recordInvocation(parsed) {
|
|
14055
14174
|
const file = process19.env.ODLA_CLI_RECORD;
|
|
14056
14175
|
if (!file) return;
|
|
@@ -14110,10 +14229,10 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
14110
14229
|
var UNITS = { d: DAY_MS, w: 7 * DAY_MS, y: 365 * DAY_MS };
|
|
14111
14230
|
function parseDeviceTtl(raw) {
|
|
14112
14231
|
if (raw === void 0 || raw === true) return void 0;
|
|
14113
|
-
const
|
|
14114
|
-
if (!
|
|
14115
|
-
if (
|
|
14116
|
-
const match = /^(\d+)\s*([dwy])$/.exec(
|
|
14232
|
+
const text4 = String(raw).trim().toLowerCase();
|
|
14233
|
+
if (!text4) return void 0;
|
|
14234
|
+
if (text4 === "forever" || text4 === "never") return 100 * 365 * DAY_MS;
|
|
14235
|
+
const match = /^(\d+)\s*([dwy])$/.exec(text4);
|
|
14117
14236
|
if (!match) {
|
|
14118
14237
|
throw new Error(
|
|
14119
14238
|
`--device-ttl expects a duration like 30d, 6w, 2y, or "forever" (got "${raw}")`
|
|
@@ -14144,6 +14263,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14144
14263
|
"wait"
|
|
14145
14264
|
], 3);
|
|
14146
14265
|
const action2 = parsed.positionals[1] ?? "";
|
|
14266
|
+
if (!acceptedAfter(["device"]).includes(action2)) rejectWord(["device"], action2);
|
|
14147
14267
|
const out = deps.stdout ?? console;
|
|
14148
14268
|
const doFetch = deps.fetch ?? fetch;
|
|
14149
14269
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
@@ -14151,7 +14271,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14151
14271
|
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
14152
14272
|
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
14153
14273
|
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
14154
|
-
|
|
14274
|
+
rejectWord(["device"], action2);
|
|
14155
14275
|
}
|
|
14156
14276
|
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14157
14277
|
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
@@ -14446,12 +14566,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
14446
14566
|
// src/runbook-import.ts
|
|
14447
14567
|
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
14448
14568
|
import { basename as basename2, join as join15 } from "path";
|
|
14449
|
-
function parseRunbook(
|
|
14450
|
-
let rest =
|
|
14569
|
+
function parseRunbook(text4, slug) {
|
|
14570
|
+
let rest = text4;
|
|
14451
14571
|
const meta = {};
|
|
14452
|
-
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(
|
|
14572
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text4);
|
|
14453
14573
|
if (fm) {
|
|
14454
|
-
rest =
|
|
14574
|
+
rest = text4.slice(fm[0].length);
|
|
14455
14575
|
for (const line2 of fm[1].split(/\r?\n/)) {
|
|
14456
14576
|
const pair = /^(\w+)\s*:\s*(.+)$/.exec(line2.trim());
|
|
14457
14577
|
if (!pair) continue;
|
|
@@ -15049,6 +15169,7 @@ async function buildContext3(parsed, deps, action2) {
|
|
|
15049
15169
|
async function runbookCommand(parsed, deps = {}) {
|
|
15050
15170
|
const action2 = parsed.positionals[1] ?? "list";
|
|
15051
15171
|
assertArgs(parsed, ALLOWED2, action2 === "ask" || action2 === "search" ? 64 : 4);
|
|
15172
|
+
if (!acceptedAfter(["runbook"]).includes(action2)) rejectWord(["runbook"], action2);
|
|
15052
15173
|
const ctx = await buildContext3(parsed, deps, action2);
|
|
15053
15174
|
const slug = parsed.positionals[2];
|
|
15054
15175
|
switch (action2) {
|
|
@@ -15138,7 +15259,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15138
15259
|
case "rm":
|
|
15139
15260
|
return runbookRemove(ctx, requireSlug(slug, "rm"));
|
|
15140
15261
|
default:
|
|
15141
|
-
|
|
15262
|
+
rejectWord(["runbook"], action2);
|
|
15142
15263
|
}
|
|
15143
15264
|
}
|
|
15144
15265
|
|
|
@@ -15759,9 +15880,7 @@ async function securityCommand(parsed, dependencies) {
|
|
|
15759
15880
|
else printHostedReport(context.stdout, report5);
|
|
15760
15881
|
return;
|
|
15761
15882
|
}
|
|
15762
|
-
if (sub !== "run")
|
|
15763
|
-
throw new Error('unknown security command. Try "odla-ai security plan", "security sources", or "security run".');
|
|
15764
|
-
}
|
|
15883
|
+
if (sub !== "run") rejectWord(["security"], sub);
|
|
15765
15884
|
const sourceId = stringOpt(parsed.options.source);
|
|
15766
15885
|
if (sourceId) await runSourceSecurityCommand(parsed, dependencies, sourceId);
|
|
15767
15886
|
else await runLocalSecurityCommand(parsed, dependencies);
|
|
@@ -15778,9 +15897,7 @@ async function githubSecurityCommand(parsed, dependencies) {
|
|
|
15778
15897
|
stringOpt(parsed.options.env)
|
|
15779
15898
|
);
|
|
15780
15899
|
}
|
|
15781
|
-
if (action2 !== "connect")
|
|
15782
|
-
throw new Error('unknown security github command. Try "odla-ai security github connect".');
|
|
15783
|
-
}
|
|
15900
|
+
if (action2 !== "connect") rejectWord(["security", "github"], action2);
|
|
15784
15901
|
assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
|
|
15785
15902
|
await requireStudioHuman(
|
|
15786
15903
|
stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -15923,6 +16040,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
15923
16040
|
}
|
|
15924
16041
|
if (command === "bug") {
|
|
15925
16042
|
const action2 = parsed.positionals[1] ?? "list";
|
|
16043
|
+
if (!acceptedAfter(["bug"]).includes(action2)) rejectWord(["bug"], action2);
|
|
15926
16044
|
const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
15927
16045
|
await pmCommand({
|
|
15928
16046
|
...parsed,
|
|
@@ -15951,7 +16069,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
15951
16069
|
return;
|
|
15952
16070
|
}
|
|
15953
16071
|
if (await projectCommand(command, parsed, runtime)) return;
|
|
15954
|
-
|
|
16072
|
+
rejectWord([], command);
|
|
15955
16073
|
}
|
|
15956
16074
|
async function provisionCommand(parsed, dependencies) {
|
|
15957
16075
|
assertArgs(parsed, [
|
|
@@ -15995,7 +16113,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
15995
16113
|
async function calendarCommand(parsed, dependencies) {
|
|
15996
16114
|
const sub = parsed.positionals[1];
|
|
15997
16115
|
if (sub !== "status" && sub !== "calendars" && sub !== "connect" && sub !== "disconnect") {
|
|
15998
|
-
|
|
16116
|
+
rejectWord(["calendar"], sub);
|
|
15999
16117
|
}
|
|
16000
16118
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open", "yes"], 2);
|
|
16001
16119
|
if (sub !== "status" && sub !== "calendars" && parsed.options.json !== void 0) throw new Error(`--json is supported only by calendar status/calendars`);
|
|
@@ -16022,6 +16140,12 @@ export {
|
|
|
16022
16140
|
getScopedPlatformToken,
|
|
16023
16141
|
SYSTEM_AI_PURPOSES,
|
|
16024
16142
|
adminAi,
|
|
16143
|
+
COMMAND_SURFACE,
|
|
16144
|
+
acceptedAfter,
|
|
16145
|
+
validateInvocation,
|
|
16146
|
+
describeProblem,
|
|
16147
|
+
invocationPath,
|
|
16148
|
+
surfacePaths,
|
|
16025
16149
|
calendarServiceConfig,
|
|
16026
16150
|
calendarBookingPageUrl,
|
|
16027
16151
|
GOOGLE_CALENDAR_EVENTS_SCOPE,
|
|
@@ -16059,12 +16183,6 @@ export {
|
|
|
16059
16183
|
monitoringWireConfig,
|
|
16060
16184
|
monitorCommand,
|
|
16061
16185
|
provision,
|
|
16062
|
-
COMMAND_SURFACE,
|
|
16063
|
-
acceptedAfter,
|
|
16064
|
-
validateInvocation,
|
|
16065
|
-
describeProblem,
|
|
16066
|
-
invocationPath,
|
|
16067
|
-
surfacePaths,
|
|
16068
16186
|
runHostedSecurity,
|
|
16069
16187
|
getHostedSecurityIntent,
|
|
16070
16188
|
getHostedSecurityPlan,
|
|
@@ -16076,4 +16194,4 @@ export {
|
|
|
16076
16194
|
isTerminalHostedSecurityStatus,
|
|
16077
16195
|
runCli
|
|
16078
16196
|
};
|
|
16079
|
-
//# sourceMappingURL=chunk-
|
|
16197
|
+
//# sourceMappingURL=chunk-7MUWCSGP.js.map
|