@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
package/dist/index.cjs
CHANGED
|
@@ -452,10 +452,10 @@ function isManagedDevVar(line2) {
|
|
|
452
452
|
const match = line2.match(/^\s*(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/);
|
|
453
453
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
454
454
|
}
|
|
455
|
-
function writePrivateText(path,
|
|
455
|
+
function writePrivateText(path, text4) {
|
|
456
456
|
(0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(path), { recursive: true });
|
|
457
457
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
458
|
-
(0, import_node_fs4.writeFileSync)(temporary,
|
|
458
|
+
(0, import_node_fs4.writeFileSync)(temporary, text4, { mode: 384 });
|
|
459
459
|
(0, import_node_fs4.chmodSync)(temporary, 384);
|
|
460
460
|
(0, import_node_fs4.renameSync)(temporary, path);
|
|
461
461
|
}
|
|
@@ -895,12 +895,12 @@ async function readAdminAiAudit(request3) {
|
|
|
895
895
|
}
|
|
896
896
|
}
|
|
897
897
|
async function responseBody(response2) {
|
|
898
|
-
const
|
|
899
|
-
if (!
|
|
898
|
+
const text4 = await response2.text();
|
|
899
|
+
if (!text4) return {};
|
|
900
900
|
try {
|
|
901
|
-
return JSON.parse(
|
|
901
|
+
return JSON.parse(text4);
|
|
902
902
|
} catch {
|
|
903
|
-
return { message:
|
|
903
|
+
return { message: text4.slice(0, 300) };
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
function apiError(status, body) {
|
|
@@ -1002,12 +1002,12 @@ function timestamp2(value2) {
|
|
|
1002
1002
|
return Number.isFinite(date.valueOf()) ? date.toISOString() : "";
|
|
1003
1003
|
}
|
|
1004
1004
|
async function responseBody2(res) {
|
|
1005
|
-
const
|
|
1006
|
-
if (!
|
|
1005
|
+
const text4 = await res.text();
|
|
1006
|
+
if (!text4) return {};
|
|
1007
1007
|
try {
|
|
1008
|
-
return JSON.parse(
|
|
1008
|
+
return JSON.parse(text4);
|
|
1009
1009
|
} catch {
|
|
1010
|
-
return { message:
|
|
1010
|
+
return { message: text4.slice(0, 300) };
|
|
1011
1011
|
}
|
|
1012
1012
|
}
|
|
1013
1013
|
function apiError2(action2, status, body) {
|
|
@@ -1183,12 +1183,12 @@ function catalogModels(body) {
|
|
|
1183
1183
|
return body.catalog.models.filter((value2) => isRecord3(value2) && typeof value2.id === "string" && typeof value2.provider === "string");
|
|
1184
1184
|
}
|
|
1185
1185
|
async function responseBody3(res) {
|
|
1186
|
-
const
|
|
1187
|
-
if (!
|
|
1186
|
+
const text4 = await res.text();
|
|
1187
|
+
if (!text4) return {};
|
|
1188
1188
|
try {
|
|
1189
|
-
return JSON.parse(
|
|
1189
|
+
return JSON.parse(text4);
|
|
1190
1190
|
} catch {
|
|
1191
|
-
return { message:
|
|
1191
|
+
return { message: text4.slice(0, 300) };
|
|
1192
1192
|
}
|
|
1193
1193
|
}
|
|
1194
1194
|
function apiError3(action2, status, body) {
|
|
@@ -1234,10 +1234,17 @@ function parseArgv(argv) {
|
|
|
1234
1234
|
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
1235
1235
|
const allowed = new Set(allowedOptions2);
|
|
1236
1236
|
for (const name of Object.keys(parsed.options)) {
|
|
1237
|
-
if (
|
|
1237
|
+
if (allowed.has(name)) continue;
|
|
1238
|
+
const accepts = [...allowed].sort().map((option) => `--${option}`).join(" ");
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`unknown option "--${name}"` + (accepts ? ` \u2014 this command accepts: ${accepts}` : " \u2014 this command takes no options")
|
|
1241
|
+
);
|
|
1238
1242
|
}
|
|
1239
1243
|
if (parsed.positionals.length > maxPositionals) {
|
|
1240
|
-
|
|
1244
|
+
const taken = parsed.positionals.slice(0, maxPositionals).join(" ");
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`unexpected argument "${parsed.positionals[maxPositionals]}" \u2014 ` + (maxPositionals ? `"odla-ai ${taken}" takes no further arguments` : "this command takes no arguments")
|
|
1247
|
+
);
|
|
1241
1248
|
}
|
|
1242
1249
|
}
|
|
1243
1250
|
function requiredString(value2, name) {
|
|
@@ -1275,6 +1282,198 @@ function addOption(options, name, value2) {
|
|
|
1275
1282
|
else options[name] = [String(current), String(value2)];
|
|
1276
1283
|
}
|
|
1277
1284
|
|
|
1285
|
+
// src/surface.ts
|
|
1286
|
+
var PM_ACTIONS = {
|
|
1287
|
+
list: {},
|
|
1288
|
+
add: {},
|
|
1289
|
+
create: {},
|
|
1290
|
+
get: {},
|
|
1291
|
+
set: {},
|
|
1292
|
+
update: {},
|
|
1293
|
+
status: {},
|
|
1294
|
+
move: {},
|
|
1295
|
+
done: {},
|
|
1296
|
+
comment: {},
|
|
1297
|
+
comments: {},
|
|
1298
|
+
history: {},
|
|
1299
|
+
link: {},
|
|
1300
|
+
ref: {},
|
|
1301
|
+
rm: {},
|
|
1302
|
+
delete: {}
|
|
1303
|
+
};
|
|
1304
|
+
var PM_TASK_ACTIONS = {
|
|
1305
|
+
...PM_ACTIONS,
|
|
1306
|
+
ready: {},
|
|
1307
|
+
claim: {},
|
|
1308
|
+
release: {}
|
|
1309
|
+
};
|
|
1310
|
+
var PM_ENTITIES = {
|
|
1311
|
+
...Object.fromEntries(
|
|
1312
|
+
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
1313
|
+
),
|
|
1314
|
+
task: PM_TASK_ACTIONS,
|
|
1315
|
+
kanban: PM_TASK_ACTIONS
|
|
1316
|
+
};
|
|
1317
|
+
var COMMAND_SURFACE = {
|
|
1318
|
+
agent: { jobs: {}, retry: {} },
|
|
1319
|
+
ai: { models: {} },
|
|
1320
|
+
admin: {
|
|
1321
|
+
ai: {
|
|
1322
|
+
show: {},
|
|
1323
|
+
set: {},
|
|
1324
|
+
credentials: {},
|
|
1325
|
+
models: {},
|
|
1326
|
+
usage: {},
|
|
1327
|
+
audit: {},
|
|
1328
|
+
credential: { set: {} }
|
|
1329
|
+
},
|
|
1330
|
+
spend: { show: {}, reset: {} }
|
|
1331
|
+
},
|
|
1332
|
+
app: {
|
|
1333
|
+
archive: {},
|
|
1334
|
+
restore: {},
|
|
1335
|
+
export: {},
|
|
1336
|
+
import: {},
|
|
1337
|
+
rename: {},
|
|
1338
|
+
"refresh-sandbox": {},
|
|
1339
|
+
"go-live": {},
|
|
1340
|
+
promote: {},
|
|
1341
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
1342
|
+
},
|
|
1343
|
+
auth: { login: {} },
|
|
1344
|
+
brand: { design: { unpack: {} } },
|
|
1345
|
+
bug: { create: {}, list: {}, report: {} },
|
|
1346
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
1347
|
+
capabilities: {},
|
|
1348
|
+
code: {
|
|
1349
|
+
connect: {},
|
|
1350
|
+
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
1351
|
+
repository: { show: {}, list: {}, bind: {} }
|
|
1352
|
+
},
|
|
1353
|
+
config: { diff: {}, plan: {}, apply: {} },
|
|
1354
|
+
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
1355
|
+
credentials: { list: {}, revoke: {} },
|
|
1356
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
1357
|
+
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
1358
|
+
discuss: {
|
|
1359
|
+
groups: {},
|
|
1360
|
+
list: {},
|
|
1361
|
+
topics: {},
|
|
1362
|
+
read: {},
|
|
1363
|
+
post: {},
|
|
1364
|
+
reply: {},
|
|
1365
|
+
resolve: {},
|
|
1366
|
+
who: {},
|
|
1367
|
+
watch: {}
|
|
1368
|
+
},
|
|
1369
|
+
doctor: {},
|
|
1370
|
+
help: {},
|
|
1371
|
+
init: {},
|
|
1372
|
+
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
1373
|
+
o11y: { status: {} },
|
|
1374
|
+
operations: { get: {}, wait: {} },
|
|
1375
|
+
platform: {
|
|
1376
|
+
status: {}
|
|
1377
|
+
},
|
|
1378
|
+
pm: {
|
|
1379
|
+
...PM_ENTITIES,
|
|
1380
|
+
project: { list: {}, add: {}, create: {}, use: {} },
|
|
1381
|
+
handoff: {},
|
|
1382
|
+
next: {},
|
|
1383
|
+
start: {},
|
|
1384
|
+
watch: {}
|
|
1385
|
+
},
|
|
1386
|
+
provision: {},
|
|
1387
|
+
runbook: {
|
|
1388
|
+
ask: {},
|
|
1389
|
+
search: {},
|
|
1390
|
+
impact: {},
|
|
1391
|
+
list: {},
|
|
1392
|
+
get: {},
|
|
1393
|
+
cat: {},
|
|
1394
|
+
new: {},
|
|
1395
|
+
edit: {},
|
|
1396
|
+
comment: {},
|
|
1397
|
+
import: {},
|
|
1398
|
+
visibility: {},
|
|
1399
|
+
publish: {},
|
|
1400
|
+
archive: {},
|
|
1401
|
+
history: {},
|
|
1402
|
+
revert: {},
|
|
1403
|
+
rm: {},
|
|
1404
|
+
lint: {}
|
|
1405
|
+
},
|
|
1406
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
1407
|
+
security: {
|
|
1408
|
+
plan: {},
|
|
1409
|
+
sources: {},
|
|
1410
|
+
run: {},
|
|
1411
|
+
status: {},
|
|
1412
|
+
report: {},
|
|
1413
|
+
github: { connect: {}, disconnect: {} }
|
|
1414
|
+
},
|
|
1415
|
+
setup: {},
|
|
1416
|
+
skill: { install: {} },
|
|
1417
|
+
smoke: {},
|
|
1418
|
+
version: {},
|
|
1419
|
+
whoami: {}
|
|
1420
|
+
};
|
|
1421
|
+
function acceptedAfter(path) {
|
|
1422
|
+
let node = COMMAND_SURFACE;
|
|
1423
|
+
for (const word of path) {
|
|
1424
|
+
node = node?.[word];
|
|
1425
|
+
if (!node) return [];
|
|
1426
|
+
}
|
|
1427
|
+
return Object.keys(node).sort();
|
|
1428
|
+
}
|
|
1429
|
+
function validateInvocation(words2) {
|
|
1430
|
+
let node = COMMAND_SURFACE;
|
|
1431
|
+
const walked = [];
|
|
1432
|
+
for (const word of words2) {
|
|
1433
|
+
if (Object.keys(node).length === 0) return null;
|
|
1434
|
+
const next = node[word];
|
|
1435
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
1436
|
+
walked.push(word);
|
|
1437
|
+
node = next;
|
|
1438
|
+
}
|
|
1439
|
+
return null;
|
|
1440
|
+
}
|
|
1441
|
+
function describeProblem(problem) {
|
|
1442
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
1443
|
+
if (!problem.accepted.length) return `"${problem.word}" is not accepted ${where}. Run "odla-ai help"`;
|
|
1444
|
+
if (!problem.word) return `"odla-ai ${problem.validPrefix}" needs one of: ${problem.accepted.join(", ")}`;
|
|
1445
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
1446
|
+
}
|
|
1447
|
+
function rejectWord(path, word, note) {
|
|
1448
|
+
const sentence = describeProblem({
|
|
1449
|
+
validPrefix: path.join(" "),
|
|
1450
|
+
word: word ?? "",
|
|
1451
|
+
accepted: acceptedAfter(path)
|
|
1452
|
+
});
|
|
1453
|
+
throw new Error(note ? `${sentence}. ${note}` : sentence);
|
|
1454
|
+
}
|
|
1455
|
+
function invocationPath(words2) {
|
|
1456
|
+
let node = COMMAND_SURFACE;
|
|
1457
|
+
const path = [];
|
|
1458
|
+
for (const word of words2) {
|
|
1459
|
+
const next = node[word];
|
|
1460
|
+
if (!next) break;
|
|
1461
|
+
path.push(word);
|
|
1462
|
+
node = next;
|
|
1463
|
+
if (Object.keys(node).length === 0) break;
|
|
1464
|
+
}
|
|
1465
|
+
return path;
|
|
1466
|
+
}
|
|
1467
|
+
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
1468
|
+
const paths2 = [];
|
|
1469
|
+
for (const [word, child] of Object.entries(node)) {
|
|
1470
|
+
const path = [...prefix, word];
|
|
1471
|
+
paths2.push(path);
|
|
1472
|
+
paths2.push(...surfacePaths(child, path));
|
|
1473
|
+
}
|
|
1474
|
+
return paths2;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1278
1477
|
// src/admin-spend.ts
|
|
1279
1478
|
async function call(ctx, method, scope) {
|
|
1280
1479
|
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
@@ -1324,9 +1523,7 @@ async function spendReset(ctx, scope) {
|
|
|
1324
1523
|
async function adminSpend(parsed, ctx) {
|
|
1325
1524
|
const action2 = parsed.positionals[2];
|
|
1326
1525
|
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1327
|
-
if (action2 !== "show" && action2 !== "reset")
|
|
1328
|
-
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1329
|
-
}
|
|
1526
|
+
if (action2 !== "show" && action2 !== "reset") rejectWord(["admin", "spend"], action2);
|
|
1330
1527
|
if (!scope) {
|
|
1331
1528
|
throw new Error(
|
|
1332
1529
|
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
@@ -2125,6 +2322,11 @@ var SET_OPTIONS = [
|
|
|
2125
2322
|
async function adminCommand(parsed, deps = {}) {
|
|
2126
2323
|
const area = parsed.positionals[1];
|
|
2127
2324
|
const action2 = parsed.positionals[2];
|
|
2325
|
+
if (area !== "ai" && area !== "spend") rejectWord(["admin"], area);
|
|
2326
|
+
if (!acceptedAfter(["admin", area]).includes(action2 ?? "")) rejectWord(["admin", area], action2);
|
|
2327
|
+
if (action2 === "credential" && !acceptedAfter(["admin", "ai", "credential"]).includes(parsed.positionals[3] ?? "")) {
|
|
2328
|
+
rejectWord(["admin", "ai", "credential"], parsed.positionals[3]);
|
|
2329
|
+
}
|
|
2128
2330
|
if (area === "spend") {
|
|
2129
2331
|
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
2130
2332
|
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2150,14 +2352,11 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2150
2352
|
out
|
|
2151
2353
|
});
|
|
2152
2354
|
}
|
|
2153
|
-
const credentialSet = action2 === "credential"
|
|
2355
|
+
const credentialSet = action2 === "credential";
|
|
2154
2356
|
const credentials = action2 === "credentials";
|
|
2155
2357
|
const models = action2 === "models";
|
|
2156
2358
|
const usage = action2 === "usage";
|
|
2157
2359
|
const audit = action2 === "audit";
|
|
2158
|
-
if (area !== "ai" || action2 !== "show" && action2 !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
2159
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
2160
|
-
}
|
|
2161
2360
|
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;
|
|
2162
2361
|
assertArgs(parsed, allowed, credentialSet ? 5 : action2 === "set" ? 4 : 3);
|
|
2163
2362
|
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2371,9 +2570,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2371
2570
|
"json"
|
|
2372
2571
|
], 2);
|
|
2373
2572
|
const action2 = parsed.positionals[1] ?? "login";
|
|
2374
|
-
if (action2 !== "login")
|
|
2375
|
-
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
2376
|
-
}
|
|
2573
|
+
if (action2 !== "login") rejectWord(["auth"], action2);
|
|
2377
2574
|
const context = await resolveOperatorContext(parsed, {
|
|
2378
2575
|
allowMissingConfig: true,
|
|
2379
2576
|
requireApp: true
|
|
@@ -2446,9 +2643,7 @@ function bothTenants(cfg) {
|
|
|
2446
2643
|
// src/agent-command.ts
|
|
2447
2644
|
async function agentCommand(parsed, deps = {}) {
|
|
2448
2645
|
const action2 = parsed.positionals[1];
|
|
2449
|
-
if (action2 !== "jobs" && action2 !== "retry")
|
|
2450
|
-
throw new Error(`unknown agent action "${action2 ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
2451
|
-
}
|
|
2646
|
+
if (action2 !== "jobs" && action2 !== "retry") rejectWord(["agent"], action2);
|
|
2452
2647
|
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token"], action2 === "jobs" ? 2 : 3);
|
|
2453
2648
|
if (action2 === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
2454
2649
|
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
@@ -2557,8 +2752,8 @@ async function appImport(options) {
|
|
|
2557
2752
|
const out = options.stdout ?? console;
|
|
2558
2753
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2559
2754
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2560
|
-
const
|
|
2561
|
-
const { format, sources } = (0, import_import.parseImport)(
|
|
2755
|
+
const text4 = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs9.readFileSync)(0, "utf8")))() : (0, import_node_fs9.readFileSync)(options.file, "utf8");
|
|
2756
|
+
const { format, sources } = (0, import_import.parseImport)(text4, options.ns);
|
|
2562
2757
|
if (format === "namespace-map" && options.ns) {
|
|
2563
2758
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
2564
2759
|
}
|
|
@@ -2619,9 +2814,7 @@ async function appOwnersCommand(parsed, dependencies = {}) {
|
|
|
2619
2814
|
await (sub === "add" ? ownersAdd(email, options) : ownersRemove(email, options));
|
|
2620
2815
|
return;
|
|
2621
2816
|
}
|
|
2622
|
-
|
|
2623
|
-
`unknown app owners subcommand "${sub}". Try "odla-ai app owners list", "odla-ai app owners add <email>", or "odla-ai app owners remove <email>".`
|
|
2624
|
-
);
|
|
2817
|
+
rejectWord(["app", "owners"], sub);
|
|
2625
2818
|
}
|
|
2626
2819
|
|
|
2627
2820
|
// src/app-rename.ts
|
|
@@ -2727,8 +2920,10 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2727
2920
|
return;
|
|
2728
2921
|
}
|
|
2729
2922
|
if (sub !== "archive" && sub !== "restore") {
|
|
2730
|
-
|
|
2731
|
-
|
|
2923
|
+
rejectWord(
|
|
2924
|
+
["app"],
|
|
2925
|
+
sub,
|
|
2926
|
+
"Permanent deletion has no CLI: it requires a signed-in owner in Studio."
|
|
2732
2927
|
);
|
|
2733
2928
|
}
|
|
2734
2929
|
assertArgs(parsed, ["config", "token", "email", "yes", "json"], 2);
|
|
@@ -2769,7 +2964,7 @@ var EXTENSIONS = {
|
|
|
2769
2964
|
"text/html": "html",
|
|
2770
2965
|
"application/json": "json"
|
|
2771
2966
|
};
|
|
2772
|
-
var encode = (
|
|
2967
|
+
var encode = (text4) => new TextEncoder().encode(text4);
|
|
2773
2968
|
function assetFileName(uuid, mime) {
|
|
2774
2969
|
const ext = EXTENSIONS[mime.split(";")[0].trim().toLowerCase()] ?? "bin";
|
|
2775
2970
|
return `${uuid.replace(/[^a-zA-Z0-9._-]/g, "_")}.${ext}`;
|
|
@@ -2889,7 +3084,8 @@ async function brandCommand(parsed, deps) {
|
|
|
2889
3084
|
await designUnpack(parsed, deps);
|
|
2890
3085
|
return;
|
|
2891
3086
|
}
|
|
2892
|
-
|
|
3087
|
+
if (subject !== "design") rejectWord(["brand"], subject);
|
|
3088
|
+
rejectWord(["brand", "design"], action2, USAGE);
|
|
2893
3089
|
}
|
|
2894
3090
|
|
|
2895
3091
|
// src/calendar-errors.ts
|
|
@@ -3679,9 +3875,9 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
3679
3875
|
}
|
|
3680
3876
|
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
|
|
3681
3877
|
}
|
|
3682
|
-
function errorCode(
|
|
3878
|
+
function errorCode(text4) {
|
|
3683
3879
|
try {
|
|
3684
|
-
const body = JSON.parse(
|
|
3880
|
+
const body = JSON.parse(text4);
|
|
3685
3881
|
return typeof body.error?.code === "string" ? body.error.code : null;
|
|
3686
3882
|
} catch {
|
|
3687
3883
|
return null;
|
|
@@ -4416,15 +4612,15 @@ function readWranglerConfig(path) {
|
|
|
4416
4612
|
return null;
|
|
4417
4613
|
}
|
|
4418
4614
|
}
|
|
4419
|
-
function stripJsonComments(
|
|
4615
|
+
function stripJsonComments(text4) {
|
|
4420
4616
|
let result = "";
|
|
4421
4617
|
let inString = false;
|
|
4422
|
-
for (let i = 0; i <
|
|
4423
|
-
const ch =
|
|
4618
|
+
for (let i = 0; i < text4.length; i++) {
|
|
4619
|
+
const ch = text4[i];
|
|
4424
4620
|
if (inString) {
|
|
4425
4621
|
result += ch;
|
|
4426
4622
|
if (ch === "\\") {
|
|
4427
|
-
result +=
|
|
4623
|
+
result += text4[i + 1] ?? "";
|
|
4428
4624
|
i++;
|
|
4429
4625
|
} else if (ch === '"') {
|
|
4430
4626
|
inString = false;
|
|
@@ -4436,14 +4632,14 @@ function stripJsonComments(text3) {
|
|
|
4436
4632
|
result += ch;
|
|
4437
4633
|
continue;
|
|
4438
4634
|
}
|
|
4439
|
-
if (ch === "/" &&
|
|
4440
|
-
while (i <
|
|
4635
|
+
if (ch === "/" && text4[i + 1] === "/") {
|
|
4636
|
+
while (i < text4.length && text4[i] !== "\n") i++;
|
|
4441
4637
|
result += "\n";
|
|
4442
4638
|
continue;
|
|
4443
4639
|
}
|
|
4444
|
-
if (ch === "/" &&
|
|
4640
|
+
if (ch === "/" && text4[i + 1] === "*") {
|
|
4445
4641
|
i += 2;
|
|
4446
|
-
while (i <
|
|
4642
|
+
while (i < text4.length && !(text4[i] === "*" && text4[i + 1] === "/")) i++;
|
|
4447
4643
|
i++;
|
|
4448
4644
|
continue;
|
|
4449
4645
|
}
|
|
@@ -4957,9 +5153,9 @@ function initProject(options) {
|
|
|
4957
5153
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
4958
5154
|
out.log("updated .gitignore for local odla credentials");
|
|
4959
5155
|
}
|
|
4960
|
-
function writeIfMissing(path,
|
|
5156
|
+
function writeIfMissing(path, text4) {
|
|
4961
5157
|
if ((0, import_node_fs14.existsSync)(path)) return;
|
|
4962
|
-
(0, import_node_fs14.writeFileSync)(path,
|
|
5158
|
+
(0, import_node_fs14.writeFileSync)(path, text4);
|
|
4963
5159
|
}
|
|
4964
5160
|
function configTemplate(input) {
|
|
4965
5161
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -5171,13 +5367,13 @@ async function secretsSetClerkKey(options) {
|
|
|
5171
5367
|
body: JSON.stringify({ value: value2 })
|
|
5172
5368
|
});
|
|
5173
5369
|
if (!res.ok) {
|
|
5174
|
-
const
|
|
5175
|
-
throw new Error(`store Clerk secret key failed (${res.status}): ${
|
|
5370
|
+
const text4 = scrubValue((await res.text().catch(() => "")).slice(0, 300), value2);
|
|
5371
|
+
throw new Error(`store Clerk secret key failed (${res.status}): ${text4 || "request failed"}`);
|
|
5176
5372
|
}
|
|
5177
5373
|
out.log(`Clerk secret key stored for ${tenantId} ($clerk_secret, reserved + write-only; the value was never echoed)`);
|
|
5178
5374
|
}
|
|
5179
|
-
function scrubValue(
|
|
5180
|
-
return redactSecrets(
|
|
5375
|
+
function scrubValue(text4, value2) {
|
|
5376
|
+
return redactSecrets(text4).split(value2).join("[value redacted]");
|
|
5181
5377
|
}
|
|
5182
5378
|
async function resolveVaultWrite(options) {
|
|
5183
5379
|
const out = options.stdout ?? console;
|
|
@@ -5469,8 +5665,8 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5469
5665
|
harnesses: installations
|
|
5470
5666
|
};
|
|
5471
5667
|
}
|
|
5472
|
-
function pathsUnder(root,
|
|
5473
|
-
return [...
|
|
5668
|
+
function pathsUnder(root, paths2) {
|
|
5669
|
+
return [...paths2].map((path) => (0, import_node_path15.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path15.sep}`) && !(0, import_node_path15.isAbsolute)(path)).sort();
|
|
5474
5670
|
}
|
|
5475
5671
|
function normalizeHarnesses(values, global) {
|
|
5476
5672
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5750,9 +5946,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5750
5946
|
return;
|
|
5751
5947
|
}
|
|
5752
5948
|
if (sub !== "push") {
|
|
5753
|
-
|
|
5754
|
-
`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".`
|
|
5755
|
-
);
|
|
5949
|
+
rejectWord(["secrets"], sub);
|
|
5756
5950
|
}
|
|
5757
5951
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
5758
5952
|
await secretsPush({
|
|
@@ -5765,7 +5959,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5765
5959
|
async function projectCommand(command, parsed, deps) {
|
|
5766
5960
|
if (command === "ai") {
|
|
5767
5961
|
const sub = parsed.positionals[1];
|
|
5768
|
-
if (sub !== "models")
|
|
5962
|
+
if (sub !== "models") rejectWord(["ai"], sub);
|
|
5769
5963
|
assertArgs(parsed, ["config", "env", "provider", "json"], 2);
|
|
5770
5964
|
await aiModels({
|
|
5771
5965
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -5779,9 +5973,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5779
5973
|
}
|
|
5780
5974
|
if (command === "config") {
|
|
5781
5975
|
const sub = parsed.positionals[1];
|
|
5782
|
-
if (sub !== "diff" && sub !== "plan" && sub !== "apply")
|
|
5783
|
-
throw new Error(`unknown config subcommand "${sub ?? ""}". Try "odla-ai config diff --json".`);
|
|
5784
|
-
}
|
|
5976
|
+
if (sub !== "diff" && sub !== "plan" && sub !== "apply") rejectWord(["config"], sub);
|
|
5785
5977
|
assertArgs(
|
|
5786
5978
|
parsed,
|
|
5787
5979
|
sub === "apply" ? ["config", "plan", "idempotency-key", "token", "email", "open", "json"] : ["config", "token", "email", "open", "json"],
|
|
@@ -5809,7 +6001,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5809
6001
|
if (command === "operations") {
|
|
5810
6002
|
const sub = parsed.positionals[1];
|
|
5811
6003
|
if (sub !== "get" && sub !== "wait") {
|
|
5812
|
-
|
|
6004
|
+
rejectWord(["operations"], sub);
|
|
5813
6005
|
}
|
|
5814
6006
|
assertArgs(
|
|
5815
6007
|
parsed,
|
|
@@ -5883,7 +6075,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5883
6075
|
}
|
|
5884
6076
|
if (command === "skill") {
|
|
5885
6077
|
const sub = parsed.positionals[1];
|
|
5886
|
-
if (sub !== "install")
|
|
6078
|
+
if (sub !== "install") rejectWord(["skill"], sub);
|
|
5887
6079
|
install(parsed, 2, deps);
|
|
5888
6080
|
return true;
|
|
5889
6081
|
}
|
|
@@ -5899,10 +6091,10 @@ var import_node_fs16 = require("fs");
|
|
|
5899
6091
|
var import_node_os5 = require("os");
|
|
5900
6092
|
var import_node_path16 = require("path");
|
|
5901
6093
|
|
|
5902
|
-
// ../harness/dist/chunk-
|
|
6094
|
+
// ../harness/dist/chunk-RXNHCGWE.js
|
|
5903
6095
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
5904
6096
|
|
|
5905
|
-
// ../harness/dist/chunk-
|
|
6097
|
+
// ../harness/dist/chunk-CR6RE3A2.js
|
|
5906
6098
|
var import_child_process = require("child_process");
|
|
5907
6099
|
var import_fs = require("fs");
|
|
5908
6100
|
var import_promises2 = require("fs/promises");
|
|
@@ -6154,12 +6346,12 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
6154
6346
|
});
|
|
6155
6347
|
if (outputBytes > 8 * 1024 * 1024) throw new Error("git file inventory exceeds 8 MiB");
|
|
6156
6348
|
if (code !== 0) throw new Error(`git file inventory failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6157
|
-
const
|
|
6158
|
-
if (
|
|
6349
|
+
const paths2 = Buffer.concat(stdout).toString("utf8").split("\0").filter(Boolean).sort();
|
|
6350
|
+
if (paths2.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
6159
6351
|
const root = (0, import_path4.resolve)(sourceDir);
|
|
6160
6352
|
const files = [];
|
|
6161
6353
|
let bytes = 0;
|
|
6162
|
-
for (const relativePath of
|
|
6354
|
+
for (const relativePath of paths2) {
|
|
6163
6355
|
if (!allowedWorkspacePath(relativePath)) continue;
|
|
6164
6356
|
const source = (0, import_path4.resolve)(root, relativePath);
|
|
6165
6357
|
if (!source.startsWith(`${root}${import_path4.sep}`)) throw new TypeError("git file path escapes workspace");
|
|
@@ -6275,7 +6467,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
6275
6467
|
}
|
|
6276
6468
|
}
|
|
6277
6469
|
|
|
6278
|
-
// ../harness/dist/chunk-
|
|
6470
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6279
6471
|
var import_crypto = require("crypto");
|
|
6280
6472
|
var import_promises5 = require("fs/promises");
|
|
6281
6473
|
var import_path5 = require("path");
|
|
@@ -6598,13 +6790,13 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6598
6790
|
if (!Number.isSafeInteger(limits.maximumFiles) || limits.maximumFiles < 1 || !Number.isSafeInteger(limits.maximumBytes) || limits.maximumBytes < 1 || snapshot.files.length > limits.maximumFiles) {
|
|
6599
6791
|
throw new CamelError("limit_exceeded", "Code snapshot exceeds its registered limits.");
|
|
6600
6792
|
}
|
|
6601
|
-
const
|
|
6793
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
6602
6794
|
let bytes = 0;
|
|
6603
6795
|
for (const file of snapshot.files) {
|
|
6604
|
-
if (!file.path || file.path.startsWith("/") || file.path.includes("\\") || file.path.split("/").some((part) => !part || part === "." || part === "..") ||
|
|
6796
|
+
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") {
|
|
6605
6797
|
throw new CamelError("state_conflict", "Code snapshot contains an invalid or duplicate path.");
|
|
6606
6798
|
}
|
|
6607
|
-
|
|
6799
|
+
paths2.add(file.path);
|
|
6608
6800
|
bytes += utf8Length(file.path) + utf8Length(file.content);
|
|
6609
6801
|
}
|
|
6610
6802
|
if (bytes > limits.maximumBytes) {
|
|
@@ -6612,7 +6804,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
6612
6804
|
}
|
|
6613
6805
|
}
|
|
6614
6806
|
|
|
6615
|
-
// ../harness/dist/chunk-
|
|
6807
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6616
6808
|
var import_child_process4 = require("child_process");
|
|
6617
6809
|
var import_promises6 = require("fs/promises");
|
|
6618
6810
|
var import_path6 = require("path");
|
|
@@ -6755,8 +6947,8 @@ function boundedInteger(value2, spec) {
|
|
|
6755
6947
|
}
|
|
6756
6948
|
function boundedNumber(value2, spec) {
|
|
6757
6949
|
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.");
|
|
6758
|
-
const
|
|
6759
|
-
if (/e/i.test(
|
|
6950
|
+
const text4 = String(value2);
|
|
6951
|
+
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.");
|
|
6760
6952
|
return value2;
|
|
6761
6953
|
}
|
|
6762
6954
|
function enumMember(value2, spec) {
|
|
@@ -6912,11 +7104,11 @@ function validateUnsafeSelector(path, value2, tool) {
|
|
|
6912
7104
|
return void 0;
|
|
6913
7105
|
}
|
|
6914
7106
|
function looksLikeDestination(value2) {
|
|
6915
|
-
const
|
|
6916
|
-
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(
|
|
7107
|
+
const text4 = value2.trim();
|
|
7108
|
+
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text4) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text4);
|
|
6917
7109
|
}
|
|
6918
7110
|
|
|
6919
|
-
// ../harness/dist/chunk-
|
|
7111
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
6920
7112
|
var import_promises10 = require("fs/promises");
|
|
6921
7113
|
var import_promises11 = require("fs/promises");
|
|
6922
7114
|
var import_path10 = require("path");
|
|
@@ -7081,9 +7273,9 @@ async function extractImports(builder, input) {
|
|
|
7081
7273
|
const sources = input.paths.filter(isSourcePath);
|
|
7082
7274
|
const known = new Set(sources);
|
|
7083
7275
|
for (const path of sources) {
|
|
7084
|
-
let
|
|
7276
|
+
let text4;
|
|
7085
7277
|
try {
|
|
7086
|
-
|
|
7278
|
+
text4 = await input.read(path);
|
|
7087
7279
|
} catch {
|
|
7088
7280
|
continue;
|
|
7089
7281
|
}
|
|
@@ -7091,13 +7283,13 @@ async function extractImports(builder, input) {
|
|
|
7091
7283
|
const file = builder.node(FILE, path, pkg ? { pkg } : void 0);
|
|
7092
7284
|
if (pkg) builder.edge(builder.node(PACKAGE, pkg), CONTAINS, file);
|
|
7093
7285
|
const specifiers = /* @__PURE__ */ new Set();
|
|
7094
|
-
for (const match of
|
|
7095
|
-
for (const match of
|
|
7286
|
+
for (const match of text4.matchAll(IMPORT_FROM)) specifiers.add(match[1]);
|
|
7287
|
+
for (const match of text4.matchAll(BARE_IMPORT)) specifiers.add(match[1]);
|
|
7096
7288
|
for (const specifier of specifiers) {
|
|
7097
7289
|
const resolved = resolveImport(path, specifier, known);
|
|
7098
7290
|
if (resolved) builder.edge(file, IMPORTS, nodeId(FILE, resolved));
|
|
7099
7291
|
}
|
|
7100
|
-
for (const name of exportedNames(
|
|
7292
|
+
for (const name of exportedNames(text4)) {
|
|
7101
7293
|
builder.edge(file, EXPORTS, builder.node(SYMBOL, name));
|
|
7102
7294
|
}
|
|
7103
7295
|
}
|
|
@@ -7138,16 +7330,16 @@ async function extractData(builder, input) {
|
|
|
7138
7330
|
};
|
|
7139
7331
|
for (const path of input.paths) {
|
|
7140
7332
|
if (!SOURCE_FILE.test(path) || input.ignore?.(path)) continue;
|
|
7141
|
-
let
|
|
7333
|
+
let text4;
|
|
7142
7334
|
try {
|
|
7143
|
-
|
|
7335
|
+
text4 = await input.read(path);
|
|
7144
7336
|
} catch {
|
|
7145
7337
|
continue;
|
|
7146
7338
|
}
|
|
7147
|
-
for (const statement of
|
|
7339
|
+
for (const statement of text4.matchAll(STATEMENT)) {
|
|
7148
7340
|
const verb = statement[1].toUpperCase().replace(/\s+/g, " ");
|
|
7149
7341
|
const start = statement.index ?? 0;
|
|
7150
|
-
const rest =
|
|
7342
|
+
const rest = text4.slice(start + statement[0].length, start + STATEMENT_WINDOW);
|
|
7151
7343
|
if (verb === "SELECT") {
|
|
7152
7344
|
for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
|
|
7153
7345
|
continue;
|
|
@@ -7163,16 +7355,16 @@ async function extractData(builder, input) {
|
|
|
7163
7355
|
for (const read3 of rest.matchAll(READ_TABLES)) touch(path, read3[1].toLowerCase(), TABLE, READS);
|
|
7164
7356
|
}
|
|
7165
7357
|
}
|
|
7166
|
-
for (const match of
|
|
7167
|
-
touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(
|
|
7358
|
+
for (const match of text4.matchAll(NS_CONST)) {
|
|
7359
|
+
touch(path, `${match[1]}.${match[2]}`, NAMESPACE, accessFor(text4, match.index ?? 0));
|
|
7168
7360
|
}
|
|
7169
|
-
for (const match of
|
|
7170
|
-
touch(path, match[1], NAMESPACE, accessFor(
|
|
7361
|
+
for (const match of text4.matchAll(NS_LITERAL)) {
|
|
7362
|
+
touch(path, match[1], NAMESPACE, accessFor(text4, match.index ?? 0));
|
|
7171
7363
|
}
|
|
7172
7364
|
}
|
|
7173
7365
|
}
|
|
7174
|
-
function accessFor(
|
|
7175
|
-
const window =
|
|
7366
|
+
function accessFor(text4, index) {
|
|
7367
|
+
const window = text4.slice(Math.max(0, index - 160), index + 40);
|
|
7176
7368
|
return /\b(?:transact|update|delete|create|insert|Ops)\b/.test(window) ? WRITES : READS;
|
|
7177
7369
|
}
|
|
7178
7370
|
async function buildCodeGraph(input) {
|
|
@@ -7184,7 +7376,7 @@ async function buildCodeGraph(input) {
|
|
|
7184
7376
|
return builder.build();
|
|
7185
7377
|
}
|
|
7186
7378
|
|
|
7187
|
-
// ../harness/dist/chunk-
|
|
7379
|
+
// ../harness/dist/chunk-ISR434K7.js
|
|
7188
7380
|
var import_crypto4 = require("crypto");
|
|
7189
7381
|
async function digestStagedWorkspace(root, limits) {
|
|
7190
7382
|
const files = [];
|
|
@@ -7512,7 +7704,7 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
7512
7704
|
if (FORBIDDEN.test(patch2) || /(?:old|new)(?: file)? mode 120000/.test(patch2)) {
|
|
7513
7705
|
throw new TypeError("patch uses a forbidden binary, link, mode, rename, or copy operation");
|
|
7514
7706
|
}
|
|
7515
|
-
const
|
|
7707
|
+
const paths2 = [];
|
|
7516
7708
|
const lines = patch2.split("\n");
|
|
7517
7709
|
for (let index = 0; index < lines.length; index += 1) {
|
|
7518
7710
|
const line2 = lines[index];
|
|
@@ -7528,10 +7720,10 @@ function validateCodePatch(rawPatch, maxBytes) {
|
|
|
7528
7720
|
if (!validHeaderPath(oldPath, path, "a") || !validHeaderPath(newPath, path, "b")) {
|
|
7529
7721
|
throw new TypeError("patch file headers do not match the declared path");
|
|
7530
7722
|
}
|
|
7531
|
-
|
|
7723
|
+
paths2.push(path);
|
|
7532
7724
|
}
|
|
7533
|
-
if (!
|
|
7534
|
-
return
|
|
7725
|
+
if (!paths2.length || new Set(paths2).size !== paths2.length) throw new TypeError("patch has no diffs or repeats a path");
|
|
7726
|
+
return paths2;
|
|
7535
7727
|
}
|
|
7536
7728
|
function validHeaderPath(value2, path, prefix) {
|
|
7537
7729
|
return value2 === "/dev/null" || value2 === `${prefix}/${path}`;
|
|
@@ -7556,11 +7748,11 @@ function describePatchFailure(patch2, detail) {
|
|
|
7556
7748
|
const hint = hunks.length > 0 && contextless ? " A hunk has no context lines; include at least one unchanged line above or below each change." : "";
|
|
7557
7749
|
return `patch did not apply: ${detail}${hint}`;
|
|
7558
7750
|
}
|
|
7559
|
-
async function applyCodePatch(workspaceDir, rawPatch,
|
|
7751
|
+
async function applyCodePatch(workspaceDir, rawPatch, paths2) {
|
|
7560
7752
|
const patch2 = stripPatchEnvelope(rawPatch);
|
|
7561
7753
|
await gitApply(workspaceDir, patch2, true);
|
|
7562
7754
|
await gitApply(workspaceDir, patch2, false);
|
|
7563
|
-
for (const path of
|
|
7755
|
+
for (const path of paths2) {
|
|
7564
7756
|
try {
|
|
7565
7757
|
const info = await (0, import_promises6.lstat)(resolveCodePath(workspaceDir, path));
|
|
7566
7758
|
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) {
|
|
@@ -7582,8 +7774,8 @@ function gitApply(cwd, patch2, check) {
|
|
|
7582
7774
|
});
|
|
7583
7775
|
let stderr2 = "";
|
|
7584
7776
|
child.stderr.setEncoding("utf8");
|
|
7585
|
-
child.stderr.on("data", (
|
|
7586
|
-
if (stderr2.length < 4e3) stderr2 +=
|
|
7777
|
+
child.stderr.on("data", (text22) => {
|
|
7778
|
+
if (stderr2.length < 4e3) stderr2 += text22.slice(0, 4e3);
|
|
7587
7779
|
});
|
|
7588
7780
|
child.once("error", reject);
|
|
7589
7781
|
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
|
|
@@ -7607,8 +7799,8 @@ async function restoreCodeWorkspaceCheckpoint(input) {
|
|
|
7607
7799
|
const workspace = await stageWorkspace(input.trustedBaseDir, input.stage);
|
|
7608
7800
|
try {
|
|
7609
7801
|
if (checkpoint.patch) {
|
|
7610
|
-
const
|
|
7611
|
-
await applyCodePatch(workspace.workspaceDir, checkpoint.patch,
|
|
7802
|
+
const paths2 = validateCodePatch(checkpoint.patch, 256 * 1024);
|
|
7803
|
+
await applyCodePatch(workspace.workspaceDir, checkpoint.patch, paths2);
|
|
7612
7804
|
}
|
|
7613
7805
|
return { workspace, checkpoint };
|
|
7614
7806
|
} catch (error) {
|
|
@@ -7757,8 +7949,8 @@ async function verifyCodeCandidate(input) {
|
|
|
7757
7949
|
try {
|
|
7758
7950
|
const baseDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
7759
7951
|
if (baseDigest !== input.trustedBaseDigest) throw new TypeError("trusted base does not match its registered digest");
|
|
7760
|
-
const
|
|
7761
|
-
await applyCodePatch(staged.workspaceDir, input.candidatePatch,
|
|
7952
|
+
const paths2 = validateCodePatch(input.candidatePatch, policy.maximumPatchBytes);
|
|
7953
|
+
await applyCodePatch(staged.workspaceDir, input.candidatePatch, paths2);
|
|
7762
7954
|
const sourceDigest = await digestStagedWorkspace(staged.workspaceDir, limits);
|
|
7763
7955
|
const policyDigest = digestPolicy(policy);
|
|
7764
7956
|
const patchDigest = digestBytes(input.candidatePatch);
|
|
@@ -7767,7 +7959,7 @@ async function verifyCodeCandidate(input) {
|
|
|
7767
7959
|
trustedBaseDigest: input.trustedBaseDigest,
|
|
7768
7960
|
patchDigest
|
|
7769
7961
|
});
|
|
7770
|
-
const changedTests = changedTestPaths(
|
|
7962
|
+
const changedTests = changedTestPaths(paths2, policy);
|
|
7771
7963
|
if (changedTests.length > policy.maximumChangedTests) throw new TypeError("candidate changes too many test files");
|
|
7772
7964
|
const recipes = [];
|
|
7773
7965
|
const logs = [];
|
|
@@ -7834,8 +8026,8 @@ function validate2(input) {
|
|
|
7834
8026
|
}
|
|
7835
8027
|
return result;
|
|
7836
8028
|
}
|
|
7837
|
-
function changedTestPaths(
|
|
7838
|
-
return
|
|
8029
|
+
function changedTestPaths(paths2, policy) {
|
|
8030
|
+
return paths2.filter((path) => policy.testPathSuffixes.some((suffix) => path.endsWith(suffix)) || policy.testPathPrefixes.some((prefix) => path.startsWith(prefix) || path.includes(`/${prefix}`))).sort();
|
|
7839
8031
|
}
|
|
7840
8032
|
function recipeReceipt(recipe2, result, artifacts) {
|
|
7841
8033
|
const status = result.timedOut ? "timed_out" : result.outputLimitExceeded ? "output_limited" : result.exitCode === 0 && artifacts.every((item) => item.status === "verified") ? "passed" : "failed";
|
|
@@ -8566,7 +8758,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
8566
8758
|
files(root) {
|
|
8567
8759
|
const existing = cache2.get(root);
|
|
8568
8760
|
if (existing) return existing;
|
|
8569
|
-
const pending = enumerate(root, limit).then((
|
|
8761
|
+
const pending = enumerate(root, limit).then((paths2) => Object.freeze(paths2));
|
|
8570
8762
|
cache2.set(root, pending);
|
|
8571
8763
|
void pending.catch(() => {
|
|
8572
8764
|
if (cache2.get(root) === pending) cache2.delete(root);
|
|
@@ -8579,7 +8771,7 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
8579
8771
|
};
|
|
8580
8772
|
}
|
|
8581
8773
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
8582
|
-
const
|
|
8774
|
+
const paths2 = [];
|
|
8583
8775
|
const walk = async (directory) => {
|
|
8584
8776
|
for (const entry of await (0, import_promises9.readdir)(directory, { withFileTypes: true })) {
|
|
8585
8777
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
@@ -8593,26 +8785,26 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
8593
8785
|
} catch {
|
|
8594
8786
|
continue;
|
|
8595
8787
|
}
|
|
8596
|
-
|
|
8597
|
-
if (
|
|
8788
|
+
paths2.push(path);
|
|
8789
|
+
if (paths2.length > limit) throw new TypeError("workspace file registry exceeds its bound");
|
|
8598
8790
|
}
|
|
8599
8791
|
}
|
|
8600
8792
|
};
|
|
8601
8793
|
await walk((0, import_path9.resolve)(root));
|
|
8602
|
-
return
|
|
8794
|
+
return paths2.sort();
|
|
8603
8795
|
}
|
|
8604
|
-
function listWorkspace(
|
|
8796
|
+
function listWorkspace(paths2, options = {}) {
|
|
8605
8797
|
const max = options.maxEntries ?? 1e3;
|
|
8606
8798
|
const prefix = options.prefix?.replace(/\/+$/, "");
|
|
8607
|
-
const scoped = prefix ?
|
|
8799
|
+
const scoped = prefix ? paths2.filter((path) => path === prefix || path.startsWith(`${prefix}/`)) : [...paths2];
|
|
8608
8800
|
return scoped.slice(0, max);
|
|
8609
8801
|
}
|
|
8610
|
-
async function searchWorkspace(root,
|
|
8802
|
+
async function searchWorkspace(root, paths2, options) {
|
|
8611
8803
|
options.signal?.throwIfAborted();
|
|
8612
8804
|
if (!options.query) throw new TypeError("search query must be a non-empty string");
|
|
8613
8805
|
const maxResults = options.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
8614
8806
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
8615
|
-
const scoped = listWorkspace(
|
|
8807
|
+
const scoped = listWorkspace(paths2, { ...options.prefix ? { prefix: options.prefix } : {}, maxEntries: paths2.length });
|
|
8616
8808
|
if (scoped.length === 0) return [];
|
|
8617
8809
|
try {
|
|
8618
8810
|
return await nativeSearch(root, scoped, { ...options, maxResults, maxFileBytes });
|
|
@@ -8622,11 +8814,11 @@ async function searchWorkspace(root, paths, options) {
|
|
|
8622
8814
|
}
|
|
8623
8815
|
}
|
|
8624
8816
|
var MAX_NATIVE_ARG_BYTES = 96 * 1024;
|
|
8625
|
-
async function nativeSearch(root,
|
|
8817
|
+
async function nativeSearch(root, paths2, options) {
|
|
8626
8818
|
const batches = [];
|
|
8627
8819
|
let batch = [];
|
|
8628
8820
|
let bytes = 0;
|
|
8629
|
-
for (const path of
|
|
8821
|
+
for (const path of paths2) {
|
|
8630
8822
|
const size = Buffer.byteLength(path) + 1;
|
|
8631
8823
|
if (batch.length > 0 && bytes + size > MAX_NATIVE_ARG_BYTES) {
|
|
8632
8824
|
batches.push(batch);
|
|
@@ -8645,7 +8837,7 @@ async function nativeSearch(root, paths, options) {
|
|
|
8645
8837
|
}
|
|
8646
8838
|
return matches;
|
|
8647
8839
|
}
|
|
8648
|
-
function nativeSearchBatch(root,
|
|
8840
|
+
function nativeSearchBatch(root, paths2, options, remaining) {
|
|
8649
8841
|
return new Promise((resolveMatches, reject) => {
|
|
8650
8842
|
const args = [
|
|
8651
8843
|
"--fixed-strings",
|
|
@@ -8658,7 +8850,7 @@ function nativeSearchBatch(root, paths, options, remaining) {
|
|
|
8658
8850
|
options.caseSensitive === false ? "--ignore-case" : "--case-sensitive",
|
|
8659
8851
|
"--",
|
|
8660
8852
|
options.query,
|
|
8661
|
-
...
|
|
8853
|
+
...paths2
|
|
8662
8854
|
];
|
|
8663
8855
|
const child = (0, import_child_process6.spawn)("rg", args, {
|
|
8664
8856
|
cwd: root,
|
|
@@ -8844,16 +9036,16 @@ function createCodePolicyGate(options) {
|
|
|
8844
9036
|
}
|
|
8845
9037
|
};
|
|
8846
9038
|
}
|
|
8847
|
-
function directoryPrefixes(
|
|
9039
|
+
function directoryPrefixes(paths2) {
|
|
8848
9040
|
const prefixes = /* @__PURE__ */ new Set(["."]);
|
|
8849
|
-
for (const path of
|
|
9041
|
+
for (const path of paths2) {
|
|
8850
9042
|
const parts = path.split("/");
|
|
8851
9043
|
for (let index = 1; index < parts.length; index += 1) prefixes.add(parts.slice(0, index).join("/"));
|
|
8852
9044
|
}
|
|
8853
9045
|
return [...prefixes].sort();
|
|
8854
9046
|
}
|
|
8855
|
-
async function safePrefix(base,
|
|
8856
|
-
const prefixes = directoryPrefixes(
|
|
9047
|
+
async function safePrefix(base, paths2, prefix) {
|
|
9048
|
+
const prefixes = directoryPrefixes(paths2);
|
|
8857
9049
|
const conversions = await conversionRegistry(
|
|
8858
9050
|
[await registeredPolicy("code.prefix.v1", "code.prefixes.v1", prefixes)],
|
|
8859
9051
|
{ "code.prefixes.v1": prefixes }
|
|
@@ -8965,7 +9157,7 @@ function response(request3, ok, content2, details) {
|
|
|
8965
9157
|
return { requestId: request3.requestId, ok, content: content2, ...details ? { details } : {} };
|
|
8966
9158
|
}
|
|
8967
9159
|
var cache = /* @__PURE__ */ new Map();
|
|
8968
|
-
function workspaceGraphs(workspaceDir,
|
|
9160
|
+
function workspaceGraphs(workspaceDir, paths2) {
|
|
8969
9161
|
const existing = cache.get(workspaceDir);
|
|
8970
9162
|
if (existing) return existing;
|
|
8971
9163
|
const read22 = (path) => (0, import_promises11.readFile)((0, import_path10.join)(workspaceDir, path), "utf8");
|
|
@@ -8973,7 +9165,7 @@ function workspaceGraphs(workspaceDir, paths) {
|
|
|
8973
9165
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
8974
9166
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
8975
9167
|
// with ground truth should build the graph themselves.
|
|
8976
|
-
graph: await buildCodeGraph({ paths, read: read22, data: { ignore: (path) => path.includes(".generated.") } })
|
|
9168
|
+
graph: await buildCodeGraph({ paths: paths2, read: read22, data: { ignore: (path) => path.includes(".generated.") } })
|
|
8977
9169
|
}))();
|
|
8978
9170
|
cache.set(workspaceDir, built);
|
|
8979
9171
|
return built;
|
|
@@ -9033,11 +9225,11 @@ async function read(context, request3, options, policy, registry) {
|
|
|
9033
9225
|
if (endLine < startLine || endLine - startLine + 1 > (options.maxReadLines ?? 2e3)) {
|
|
9034
9226
|
throw new TypeError("requested line range exceeds its bound");
|
|
9035
9227
|
}
|
|
9036
|
-
const
|
|
9037
|
-
if (!
|
|
9228
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9229
|
+
if (!paths2.includes(path)) {
|
|
9038
9230
|
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.`);
|
|
9039
9231
|
}
|
|
9040
|
-
const allowed = await policy.read(policyContext(context, request3, options, { paths, path, startLine, endLine }));
|
|
9232
|
+
const allowed = await policy.read(policyContext(context, request3, options, { paths: paths2, path, startLine, endLine }));
|
|
9041
9233
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9042
9234
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
9043
9235
|
const info = await (0, import_promises10.stat)(target);
|
|
@@ -9059,16 +9251,16 @@ async function list(context, request3, options, policy, registry) {
|
|
|
9059
9251
|
const prefix = typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
9060
9252
|
const maxEntries = optionalInteger(request3.input.maxEntries) ?? 1e3;
|
|
9061
9253
|
if (maxEntries > 5e3) throw new TypeError("maxEntries exceeds its bound");
|
|
9062
|
-
const
|
|
9063
|
-
const allowed = await policy.list(policyContext(context, request3, options, { paths, ...prefix ? { prefix } : {} }));
|
|
9254
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9255
|
+
const allowed = await policy.list(policyContext(context, request3, options, { paths: paths2, ...prefix ? { prefix } : {} }));
|
|
9064
9256
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9065
|
-
const entries = listWorkspace(
|
|
9257
|
+
const entries = listWorkspace(paths2, { ...prefix ? { prefix } : {}, maxEntries });
|
|
9066
9258
|
if (!entries.length) {
|
|
9067
9259
|
return response(request3, true, prefix ? `No files under "${prefix}".` : "Workspace is empty.", { count: 0 });
|
|
9068
9260
|
}
|
|
9069
|
-
const truncated = entries.length <
|
|
9070
|
-
const hint = !prefix &&
|
|
9071
|
-
\u2026 ${
|
|
9261
|
+
const truncated = entries.length < paths2.length && entries.length === maxEntries;
|
|
9262
|
+
const hint = !prefix && paths2.length > 500 ? `
|
|
9263
|
+
\u2026 ${paths2.length} files total. sandbox.overview is far cheaper for orientation; use a prefix here once you know the area.` : "";
|
|
9072
9264
|
return response(
|
|
9073
9265
|
request3,
|
|
9074
9266
|
true,
|
|
@@ -9086,10 +9278,10 @@ async function search(context, request3, options, policy, registry) {
|
|
|
9086
9278
|
const maxResults = optionalInteger(request3.input.maxResults) ?? 100;
|
|
9087
9279
|
if (maxResults > 500) throw new TypeError("maxResults exceeds its bound");
|
|
9088
9280
|
const caseSensitive = request3.input.caseSensitive === void 0 ? true : request3.input.caseSensitive === true;
|
|
9089
|
-
const
|
|
9090
|
-
const allowed = await policy.search(policyContext(context, request3, options, { paths, query, ...prefix ? { prefix } : {} }));
|
|
9281
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9282
|
+
const allowed = await policy.search(policyContext(context, request3, options, { paths: paths2, query, ...prefix ? { prefix } : {} }));
|
|
9091
9283
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9092
|
-
const matches = await searchWorkspace(context.workspaceDir,
|
|
9284
|
+
const matches = await searchWorkspace(context.workspaceDir, paths2, {
|
|
9093
9285
|
query,
|
|
9094
9286
|
maxResults,
|
|
9095
9287
|
caseSensitive,
|
|
@@ -9111,8 +9303,8 @@ async function graphQuery(context, request3, options, policy, registry) {
|
|
|
9111
9303
|
selector: query
|
|
9112
9304
|
}));
|
|
9113
9305
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9114
|
-
const
|
|
9115
|
-
const graphs = await workspaceGraphs(context.workspaceDir,
|
|
9306
|
+
const paths2 = await registry.files(context.workspaceDir);
|
|
9307
|
+
const graphs = await workspaceGraphs(context.workspaceDir, paths2);
|
|
9116
9308
|
if (request3.tool === "sandbox.overview") {
|
|
9117
9309
|
return response(request3, true, renderOverview(graphs, query || void 0));
|
|
9118
9310
|
}
|
|
@@ -9174,16 +9366,16 @@ function toolFailureMessage(reason) {
|
|
|
9174
9366
|
async function patch(context, request3, options, policy, registry) {
|
|
9175
9367
|
exactKeys(request3.input, ["patch"]);
|
|
9176
9368
|
const value2 = stringField(request3.input, "patch");
|
|
9177
|
-
const
|
|
9178
|
-
if (
|
|
9369
|
+
const paths2 = validateCodePatch(value2, options.maxPatchBytes ?? 256 * 1024);
|
|
9370
|
+
if (paths2.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
9179
9371
|
throw new TypeError("patch targets a read-only reference source");
|
|
9180
9372
|
}
|
|
9181
9373
|
const allowed = await policy.patch(policyContext(context, request3, options, { patch: value2 }));
|
|
9182
9374
|
if (!allowed) return response(request3, false, "tool denied by CaMeL policy");
|
|
9183
|
-
await applyCodePatch(context.workspaceDir, value2,
|
|
9375
|
+
await applyCodePatch(context.workspaceDir, value2, paths2);
|
|
9184
9376
|
registry.invalidate(context.workspaceDir);
|
|
9185
9377
|
forgetWorkspaceGraphs(context.workspaceDir);
|
|
9186
|
-
return response(request3, true, `Applied patch to ${
|
|
9378
|
+
return response(request3, true, `Applied patch to ${paths2.length} file(s).`, { paths: paths2 });
|
|
9187
9379
|
}
|
|
9188
9380
|
async function recipe(context, request3, options, recipes, policy) {
|
|
9189
9381
|
exactKeys(request3.input, ["recipeId"]);
|
|
@@ -9532,11 +9724,126 @@ async function startGoalPursuit(input) {
|
|
|
9532
9724
|
async function appendCodeRuntimeEvent(control, command, event, refs) {
|
|
9533
9725
|
const eventId = `${command.commandId.slice(0, 45)}:${refs.length + 1}`;
|
|
9534
9726
|
refs.push(eventId);
|
|
9535
|
-
const
|
|
9727
|
+
const attributed = { ...event, interactionId: command.commandId };
|
|
9728
|
+
const bounded = attributed.type === "message" ? { ...attributed, body: attributed.body.trim().slice(0, 2e4) || `${attributed.actor} event` } : attributed;
|
|
9536
9729
|
await control.appendSessionEvent(command.sessionId, eventId, bounded);
|
|
9537
9730
|
}
|
|
9538
9731
|
var digestRuntimeValue = (value2) => `sha256:${(0, import_crypto4.createHash)("sha256").update(value2).digest("hex")}`;
|
|
9539
9732
|
var runtimeErrorMessage = (value2) => value2 instanceof Error ? value2.message : String(value2);
|
|
9733
|
+
var text3 = (value2, maximum) => {
|
|
9734
|
+
if (typeof value2 !== "string") return void 0;
|
|
9735
|
+
const bounded = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
9736
|
+
return bounded ? bounded.slice(0, maximum) : void 0;
|
|
9737
|
+
};
|
|
9738
|
+
var integer2 = (value2) => Number.isSafeInteger(value2) && Number(value2) >= 0 ? Number(value2) : void 0;
|
|
9739
|
+
var record32 = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
|
|
9740
|
+
var excerpt = (value2, tail = false) => {
|
|
9741
|
+
if (typeof value2 !== "string") return void 0;
|
|
9742
|
+
const safe = value2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ");
|
|
9743
|
+
const source = (tail ? safe.slice(-1e4) : safe.slice(0, 1e4)).trim();
|
|
9744
|
+
if (!source) return void 0;
|
|
9745
|
+
const lines = source.split("\n").filter((line2) => line2.trim()).map((line2) => line2.slice(0, 240));
|
|
9746
|
+
const selected = tail ? lines.slice(-10) : lines.slice(0, 10);
|
|
9747
|
+
return text3(selected.join("\n"), 2400);
|
|
9748
|
+
};
|
|
9749
|
+
var paths = (value2) => {
|
|
9750
|
+
if (!Array.isArray(value2)) return void 0;
|
|
9751
|
+
const items = value2.flatMap((item) => {
|
|
9752
|
+
const path = text3(item, 1024);
|
|
9753
|
+
return path ? [path] : [];
|
|
9754
|
+
}).slice(0, 12);
|
|
9755
|
+
return items.length ? items : void 0;
|
|
9756
|
+
};
|
|
9757
|
+
function patchStats(value2) {
|
|
9758
|
+
if (typeof value2 !== "string") return {};
|
|
9759
|
+
let additions = 0;
|
|
9760
|
+
let deletions = 0;
|
|
9761
|
+
for (const line2 of value2.slice(0, 262144).split("\n")) {
|
|
9762
|
+
if (line2.startsWith("+++") || line2.startsWith("---")) continue;
|
|
9763
|
+
if (line2.startsWith("+")) additions += 1;
|
|
9764
|
+
else if (line2.startsWith("-")) deletions += 1;
|
|
9765
|
+
}
|
|
9766
|
+
return { ...additions ? { additions } : {}, ...deletions ? { deletions } : {} };
|
|
9767
|
+
}
|
|
9768
|
+
function searchResults(value2) {
|
|
9769
|
+
if (typeof value2 !== "string") return void 0;
|
|
9770
|
+
const results = value2.split("\n").flatMap((line2) => {
|
|
9771
|
+
const match = /^([^:\n]{1,1024}):(\d+):\s?(.*)$/.exec(line2);
|
|
9772
|
+
if (!match) return [];
|
|
9773
|
+
const lineNumber = Number(match[2]);
|
|
9774
|
+
const itemText = text3(match[3], 240);
|
|
9775
|
+
if (!Number.isSafeInteger(lineNumber) || lineNumber < 1) return [];
|
|
9776
|
+
return [{ path: match[1], line: lineNumber, ...itemText ? { text: itemText } : {} }];
|
|
9777
|
+
}).slice(0, 5);
|
|
9778
|
+
return results.length ? results : void 0;
|
|
9779
|
+
}
|
|
9780
|
+
function codeToolRequestPresentation(request3) {
|
|
9781
|
+
const input = request3.input;
|
|
9782
|
+
if (request3.tool === "sandbox.read") {
|
|
9783
|
+
const path = text3(input.path, 1024);
|
|
9784
|
+
if (!path) return void 0;
|
|
9785
|
+
const startLine = integer2(input.startLine);
|
|
9786
|
+
const endLine = integer2(input.endLine);
|
|
9787
|
+
return { kind: "read", path, ...startLine ? { startLine } : {}, ...endLine ? { endLine } : {} };
|
|
9788
|
+
}
|
|
9789
|
+
if (request3.tool === "sandbox.list") {
|
|
9790
|
+
const scope = text3(input.prefix, 1024);
|
|
9791
|
+
return { kind: "list", ...scope ? { scope } : {} };
|
|
9792
|
+
}
|
|
9793
|
+
if (request3.tool === "sandbox.search" || request3.tool === "sandbox.overview" || request3.tool === "sandbox.where_is" || request3.tool === "sandbox.who_imports" || request3.tool === "sandbox.who_touches") {
|
|
9794
|
+
const query = text3(input.query, 512);
|
|
9795
|
+
const scope = request3.tool === "sandbox.search" ? text3(input.prefix, 1024) : void 0;
|
|
9796
|
+
if (request3.tool === "sandbox.search" && !query) return void 0;
|
|
9797
|
+
return { kind: "query", ...query ? { query } : {}, ...scope ? { scope } : {} };
|
|
9798
|
+
}
|
|
9799
|
+
if (request3.tool === "sandbox.apply_patch") {
|
|
9800
|
+
return { kind: "patch", ...patchStats(input.patch) };
|
|
9801
|
+
}
|
|
9802
|
+
const recipeId = text3(input.recipeId, 120);
|
|
9803
|
+
return recipeId ? { kind: "recipe", recipeId } : void 0;
|
|
9804
|
+
}
|
|
9805
|
+
function codeToolResultPresentation(request3, response2) {
|
|
9806
|
+
const started = codeToolRequestPresentation(request3);
|
|
9807
|
+
if (!started || !response2.ok) return started;
|
|
9808
|
+
const details = record32(response2.details);
|
|
9809
|
+
if (started.kind === "read") {
|
|
9810
|
+
return {
|
|
9811
|
+
...started,
|
|
9812
|
+
...integer2(details?.startLine) ? { startLine: integer2(details?.startLine) } : {},
|
|
9813
|
+
...integer2(details?.endLine) ? { endLine: integer2(details?.endLine) } : {},
|
|
9814
|
+
...excerpt(response2.content) ? { excerpt: excerpt(response2.content) } : {}
|
|
9815
|
+
};
|
|
9816
|
+
}
|
|
9817
|
+
if (started.kind === "list") {
|
|
9818
|
+
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);
|
|
9819
|
+
return {
|
|
9820
|
+
...started,
|
|
9821
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9822
|
+
...listed.length ? { paths: listed } : {}
|
|
9823
|
+
};
|
|
9824
|
+
}
|
|
9825
|
+
if (started.kind === "query") {
|
|
9826
|
+
const results = request3.tool === "sandbox.search" ? searchResults(response2.content) : void 0;
|
|
9827
|
+
const resultExcerpt = request3.tool === "sandbox.search" ? void 0 : excerpt(response2.content);
|
|
9828
|
+
return {
|
|
9829
|
+
...started,
|
|
9830
|
+
...integer2(details?.count) !== void 0 ? { count: integer2(details?.count) } : {},
|
|
9831
|
+
...results ? { results } : {},
|
|
9832
|
+
...resultExcerpt ? { excerpt: resultExcerpt } : {}
|
|
9833
|
+
};
|
|
9834
|
+
}
|
|
9835
|
+
if (started.kind === "patch") {
|
|
9836
|
+
return { ...started, ...paths(details?.paths) ? { paths: paths(details?.paths) } : {} };
|
|
9837
|
+
}
|
|
9838
|
+
const output = response2.content.replace(/^Recipe [^\n]*\.?\s*/u, "");
|
|
9839
|
+
return {
|
|
9840
|
+
...started,
|
|
9841
|
+
...integer2(details?.exitCode) !== void 0 ? { exitCode: integer2(details?.exitCode) } : {},
|
|
9842
|
+
...typeof details?.timedOut === "boolean" ? { timedOut: details.timedOut } : {},
|
|
9843
|
+
...typeof details?.outputLimitExceeded === "boolean" ? { outputLimitExceeded: details.outputLimitExceeded } : {},
|
|
9844
|
+
...excerpt(output, true) ? { excerpt: excerpt(output, true) } : {}
|
|
9845
|
+
};
|
|
9846
|
+
}
|
|
9540
9847
|
var TheseusRuntimeEngine = class {
|
|
9541
9848
|
constructor(options) {
|
|
9542
9849
|
this.options = options;
|
|
@@ -9770,18 +10077,29 @@ var TheseusRuntimeEngine = class {
|
|
|
9770
10077
|
return {
|
|
9771
10078
|
execute: async (context, request3) => {
|
|
9772
10079
|
const startedAt = Date.now();
|
|
10080
|
+
const operationId = digestRuntimeValue(`${command.commandId}:${request3.requestId}`);
|
|
10081
|
+
const startedPresentation = codeToolRequestPresentation(request3);
|
|
9773
10082
|
await this.#event(
|
|
9774
10083
|
command,
|
|
9775
|
-
{
|
|
10084
|
+
{
|
|
10085
|
+
type: "tool",
|
|
10086
|
+
phase: "started",
|
|
10087
|
+
tool: request3.tool,
|
|
10088
|
+
operationId,
|
|
10089
|
+
...startedPresentation ? { presentation: startedPresentation } : {}
|
|
10090
|
+
},
|
|
9776
10091
|
active.conversationRefs
|
|
9777
10092
|
).catch(() => void 0);
|
|
9778
10093
|
const response2 = await broker.execute(context, request3);
|
|
10094
|
+
const completedPresentation = codeToolResultPresentation(request3, response2);
|
|
9779
10095
|
await this.#event(command, {
|
|
9780
10096
|
type: "tool",
|
|
9781
10097
|
phase: "completed",
|
|
9782
10098
|
tool: request3.tool,
|
|
9783
10099
|
ok: response2.ok,
|
|
9784
|
-
durationMs: Date.now() - startedAt
|
|
10100
|
+
durationMs: Date.now() - startedAt,
|
|
10101
|
+
operationId,
|
|
10102
|
+
...completedPresentation ? { presentation: completedPresentation } : {}
|
|
9785
10103
|
}, active.conversationRefs).catch(() => void 0);
|
|
9786
10104
|
return response2;
|
|
9787
10105
|
}
|
|
@@ -10315,9 +10633,7 @@ function grantsUrl(cfg, suffix = "") {
|
|
|
10315
10633
|
async function codeGrantCommand(parsed, deps = {}) {
|
|
10316
10634
|
const action2 = parsed.positionals[2];
|
|
10317
10635
|
if (!isAction(action2)) {
|
|
10318
|
-
|
|
10319
|
-
`unknown code grant action "${action2 ?? ""}". Try "odla-ai code grant list --env dev".`
|
|
10320
|
-
);
|
|
10636
|
+
rejectWord(["code", "grant"], action2);
|
|
10321
10637
|
}
|
|
10322
10638
|
const decides = action2 === "approve" || action2 === "revoke";
|
|
10323
10639
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open"], decides ? 4 : 3);
|
|
@@ -10437,9 +10753,7 @@ function repositoryUrl(cfg, suffix = "") {
|
|
|
10437
10753
|
async function codeRepositoryCommand(parsed, deps = {}) {
|
|
10438
10754
|
const action2 = parsed.positionals[2];
|
|
10439
10755
|
if (!isAction2(action2)) {
|
|
10440
|
-
|
|
10441
|
-
`unknown code repository action "${action2 ?? ""}". Try "odla-ai code repository show --env dev".`
|
|
10442
|
-
);
|
|
10756
|
+
rejectWord(["code", "repository"], action2);
|
|
10443
10757
|
}
|
|
10444
10758
|
assertArgs(parsed, ["config", "env", "repo", "json", "token", "email", "open"], 3);
|
|
10445
10759
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
@@ -10514,9 +10828,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10514
10828
|
});
|
|
10515
10829
|
}
|
|
10516
10830
|
if (sub !== "connect") {
|
|
10517
|
-
|
|
10518
|
-
`unknown code subcommand "${sub ?? ""}". Try "odla-ai code connect --env dev" or "odla-ai code grant list --env dev".`
|
|
10519
|
-
);
|
|
10831
|
+
rejectWord(["code"], sub);
|
|
10520
10832
|
}
|
|
10521
10833
|
assertArgs(parsed, [
|
|
10522
10834
|
"config",
|
|
@@ -10653,9 +10965,7 @@ async function contextCommand(parsed, deps = {}) {
|
|
|
10653
10965
|
return;
|
|
10654
10966
|
}
|
|
10655
10967
|
if (action2 !== "show") {
|
|
10656
|
-
|
|
10657
|
-
`unknown context action "${action2 ?? ""}". Try show|list|save|remove.`
|
|
10658
|
-
);
|
|
10968
|
+
rejectWord(["context"], action2);
|
|
10659
10969
|
}
|
|
10660
10970
|
const context = await resolveOperatorContext(parsed, {
|
|
10661
10971
|
allowMissingConfig: true,
|
|
@@ -10711,9 +11021,7 @@ async function responseError(response2) {
|
|
|
10711
11021
|
}
|
|
10712
11022
|
async function credentialCommand(parsed, deps = {}) {
|
|
10713
11023
|
const action2 = parsed.positionals[1] ?? "list";
|
|
10714
|
-
if (action2 !== "list" && action2 !== "revoke")
|
|
10715
|
-
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
10716
|
-
}
|
|
11024
|
+
if (action2 !== "list" && action2 !== "revoke") rejectWord(["credentials"], action2);
|
|
10717
11025
|
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
10718
11026
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
10719
11027
|
const doFetch = deps.fetch ?? fetch;
|
|
@@ -11715,7 +12023,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11715
12023
|
assertArgs(parsed, ALLOWED, 3);
|
|
11716
12024
|
const action2 = parsed.positionals[1];
|
|
11717
12025
|
const id2 = parsed.positionals[2];
|
|
11718
|
-
if (!
|
|
12026
|
+
if (!acceptedAfter(["discuss"]).includes(action2 ?? "")) rejectWord(["discuss"], action2);
|
|
11719
12027
|
const ctx = await buildContext(parsed, deps);
|
|
11720
12028
|
switch (action2) {
|
|
11721
12029
|
case "groups":
|
|
@@ -11739,7 +12047,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11739
12047
|
return;
|
|
11740
12048
|
}
|
|
11741
12049
|
default:
|
|
11742
|
-
|
|
12050
|
+
rejectWord(["discuss"], action2);
|
|
11743
12051
|
}
|
|
11744
12052
|
}
|
|
11745
12053
|
|
|
@@ -11815,8 +12123,8 @@ function collectFields(parsed, allowClear) {
|
|
|
11815
12123
|
if (allowClear) out[spec.key] = null;
|
|
11816
12124
|
continue;
|
|
11817
12125
|
}
|
|
11818
|
-
const
|
|
11819
|
-
out[spec.key] = spec.num ? Number(
|
|
12126
|
+
const text4 = stringOpt(value2);
|
|
12127
|
+
out[spec.key] = spec.num ? Number(text4) : text4;
|
|
11820
12128
|
}
|
|
11821
12129
|
return out;
|
|
11822
12130
|
}
|
|
@@ -12649,7 +12957,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12649
12957
|
assertArgs(parsed, COMMON_OPTIONS, 4);
|
|
12650
12958
|
return pmProjectUse(await buildContext2(parsed, deps), requireId2(parsed.positionals[3], action3));
|
|
12651
12959
|
}
|
|
12652
|
-
|
|
12960
|
+
rejectWord(["pm", "project"], action3);
|
|
12653
12961
|
}
|
|
12654
12962
|
if (word === "next") {
|
|
12655
12963
|
assertArgs(parsed, [...COMMON_OPTIONS, "app", "project", "verbose"], 2);
|
|
@@ -12680,10 +12988,10 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12680
12988
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
12681
12989
|
}
|
|
12682
12990
|
const entity = ALIASES[word];
|
|
12683
|
-
if (!entity)
|
|
12991
|
+
if (!entity) rejectWord(["pm"], word);
|
|
12684
12992
|
const requestedAction = parsed.positionals[2] ?? "list";
|
|
12685
12993
|
const action2 = canonicalAction(requestedAction);
|
|
12686
|
-
if (!action2)
|
|
12994
|
+
if (!action2) rejectWord(["pm", word], requestedAction);
|
|
12687
12995
|
assertArgs(parsed, allowedOptions(entity, action2), 4);
|
|
12688
12996
|
if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
|
|
12689
12997
|
throw new Error(`pm ${action2} is only valid for tasks`);
|
|
@@ -12769,12 +13077,7 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
12769
13077
|
if (action2 === "status") {
|
|
12770
13078
|
return platformStatus(parsed, deps);
|
|
12771
13079
|
}
|
|
12772
|
-
|
|
12773
|
-
`unknown platform action "${[
|
|
12774
|
-
action2,
|
|
12775
|
-
parsed.positionals[2]
|
|
12776
|
-
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
12777
|
-
);
|
|
13080
|
+
rejectWord(["platform"], action2);
|
|
12778
13081
|
}
|
|
12779
13082
|
async function platformStatus(parsed, deps) {
|
|
12780
13083
|
assertArgs(
|
|
@@ -13111,9 +13414,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
13111
13414
|
);
|
|
13112
13415
|
const action2 = parsed.positionals[1];
|
|
13113
13416
|
if (action2 !== "status") {
|
|
13114
|
-
|
|
13115
|
-
`unknown o11y action "${action2 ?? ""}". Try "odla-ai o11y status --json".`
|
|
13116
|
-
);
|
|
13417
|
+
rejectWord(["o11y"], action2);
|
|
13117
13418
|
}
|
|
13118
13419
|
const minutes = statusMinutes(
|
|
13119
13420
|
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
@@ -13228,14 +13529,14 @@ function statusMinutes(value2) {
|
|
|
13228
13529
|
}
|
|
13229
13530
|
async function read2(url, headers, doFetch) {
|
|
13230
13531
|
const response2 = await doFetch(url, { headers });
|
|
13231
|
-
const
|
|
13532
|
+
const text4 = await response2.text();
|
|
13232
13533
|
let body = {};
|
|
13233
|
-
if (
|
|
13534
|
+
if (text4) {
|
|
13234
13535
|
try {
|
|
13235
|
-
const value2 = JSON.parse(
|
|
13536
|
+
const value2 = JSON.parse(text4);
|
|
13236
13537
|
body = value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : { value: value2 };
|
|
13237
13538
|
} catch {
|
|
13238
|
-
body = { message:
|
|
13539
|
+
body = { message: text4.slice(0, 300) };
|
|
13239
13540
|
}
|
|
13240
13541
|
}
|
|
13241
13542
|
return { httpStatus: response2.status, body };
|
|
@@ -13366,9 +13667,7 @@ var OPTIONS = [
|
|
|
13366
13667
|
async function monitorCommand(parsed, deps = {}) {
|
|
13367
13668
|
assertArgs(parsed, OPTIONS, 3);
|
|
13368
13669
|
const action2 = parsed.positionals[1] ?? "status";
|
|
13369
|
-
if (!["
|
|
13370
|
-
throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
|
|
13371
|
-
}
|
|
13670
|
+
if (!acceptedAfter(["monitor"]).includes(action2)) rejectWord(["monitor"], action2);
|
|
13372
13671
|
const context = await resolveOperatorContext(parsed, {
|
|
13373
13672
|
allowMissingConfig: action2 !== "plan" && action2 !== "apply",
|
|
13374
13673
|
requireApp: true
|
|
@@ -13465,13 +13764,13 @@ async function monitorCommand(parsed, deps = {}) {
|
|
|
13465
13764
|
}
|
|
13466
13765
|
async function request2(url, init, doFetch) {
|
|
13467
13766
|
const response2 = await doFetch(url, init);
|
|
13468
|
-
const
|
|
13767
|
+
const text4 = await response2.text();
|
|
13469
13768
|
let body = {};
|
|
13470
13769
|
try {
|
|
13471
|
-
const parsed =
|
|
13770
|
+
const parsed = text4 ? JSON.parse(text4) : {};
|
|
13472
13771
|
body = record10(parsed) ? parsed : { value: parsed };
|
|
13473
13772
|
} catch {
|
|
13474
|
-
body = { message:
|
|
13773
|
+
body = { message: text4.slice(0, 500) };
|
|
13475
13774
|
}
|
|
13476
13775
|
if (!response2.ok) {
|
|
13477
13776
|
const error = record10(body.error) ? body.error : body;
|
|
@@ -13674,8 +13973,8 @@ function runtimeUrl(cfg, suffix = "") {
|
|
|
13674
13973
|
return `${cfg.platformUrl}/registry/apps/${encodeURIComponent(cfg.app.id)}/runtime-credentials${suffix}`;
|
|
13675
13974
|
}
|
|
13676
13975
|
async function safeError(response2) {
|
|
13677
|
-
const
|
|
13678
|
-
return redactSecrets(
|
|
13976
|
+
const text4 = await response2.text();
|
|
13977
|
+
return redactSecrets(text4.slice(0, 1e3));
|
|
13679
13978
|
}
|
|
13680
13979
|
async function finish(doFetch, cfg, token, sessionId, method) {
|
|
13681
13980
|
return doFetch(runtimeUrl(cfg, `/${encodeURIComponent(sessionId)}`), {
|
|
@@ -13998,186 +14297,6 @@ async function provision(options) {
|
|
|
13998
14297
|
// src/record.ts
|
|
13999
14298
|
var import_node_fs18 = require("fs");
|
|
14000
14299
|
var import_node_process18 = __toESM(require("process"), 1);
|
|
14001
|
-
|
|
14002
|
-
// src/surface.ts
|
|
14003
|
-
var PM_ACTIONS = {
|
|
14004
|
-
list: {},
|
|
14005
|
-
add: {},
|
|
14006
|
-
create: {},
|
|
14007
|
-
get: {},
|
|
14008
|
-
set: {},
|
|
14009
|
-
update: {},
|
|
14010
|
-
status: {},
|
|
14011
|
-
move: {},
|
|
14012
|
-
done: {},
|
|
14013
|
-
comment: {},
|
|
14014
|
-
comments: {},
|
|
14015
|
-
ref: {},
|
|
14016
|
-
rm: {},
|
|
14017
|
-
delete: {}
|
|
14018
|
-
};
|
|
14019
|
-
var PM_TASK_ACTIONS = {
|
|
14020
|
-
...PM_ACTIONS,
|
|
14021
|
-
ready: {},
|
|
14022
|
-
claim: {},
|
|
14023
|
-
release: {}
|
|
14024
|
-
};
|
|
14025
|
-
var PM_ENTITIES = {
|
|
14026
|
-
...Object.fromEntries(
|
|
14027
|
-
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
14028
|
-
),
|
|
14029
|
-
task: PM_TASK_ACTIONS,
|
|
14030
|
-
kanban: PM_TASK_ACTIONS
|
|
14031
|
-
};
|
|
14032
|
-
var COMMAND_SURFACE = {
|
|
14033
|
-
agent: { jobs: {}, retry: {} },
|
|
14034
|
-
ai: { models: {} },
|
|
14035
|
-
admin: {
|
|
14036
|
-
ai: {
|
|
14037
|
-
show: {},
|
|
14038
|
-
set: {},
|
|
14039
|
-
credentials: {},
|
|
14040
|
-
models: {},
|
|
14041
|
-
usage: {},
|
|
14042
|
-
audit: {},
|
|
14043
|
-
credential: { set: {} }
|
|
14044
|
-
}
|
|
14045
|
-
},
|
|
14046
|
-
app: {
|
|
14047
|
-
archive: {},
|
|
14048
|
-
restore: {},
|
|
14049
|
-
export: {},
|
|
14050
|
-
import: {},
|
|
14051
|
-
rename: {},
|
|
14052
|
-
"refresh-sandbox": {},
|
|
14053
|
-
"go-live": {},
|
|
14054
|
-
promote: {},
|
|
14055
|
-
owners: { list: {}, add: {}, remove: {} }
|
|
14056
|
-
},
|
|
14057
|
-
auth: { login: {} },
|
|
14058
|
-
brand: { design: { unpack: {} } },
|
|
14059
|
-
bug: { create: {}, list: {}, report: {} },
|
|
14060
|
-
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
14061
|
-
capabilities: {},
|
|
14062
|
-
code: {
|
|
14063
|
-
connect: {},
|
|
14064
|
-
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
14065
|
-
repository: { show: {}, list: {}, bind: {} }
|
|
14066
|
-
},
|
|
14067
|
-
config: { diff: {}, plan: {}, apply: {} },
|
|
14068
|
-
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
14069
|
-
credentials: { list: {}, revoke: {} },
|
|
14070
|
-
device: { enroll: {}, list: {}, revoke: {} },
|
|
14071
|
-
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
14072
|
-
discuss: {
|
|
14073
|
-
groups: {},
|
|
14074
|
-
list: {},
|
|
14075
|
-
topics: {},
|
|
14076
|
-
read: {},
|
|
14077
|
-
post: {},
|
|
14078
|
-
reply: {},
|
|
14079
|
-
resolve: {},
|
|
14080
|
-
who: {},
|
|
14081
|
-
watch: {}
|
|
14082
|
-
},
|
|
14083
|
-
doctor: {},
|
|
14084
|
-
help: {},
|
|
14085
|
-
init: {},
|
|
14086
|
-
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
14087
|
-
o11y: { status: {} },
|
|
14088
|
-
operations: { get: {}, wait: {} },
|
|
14089
|
-
platform: {
|
|
14090
|
-
status: {}
|
|
14091
|
-
},
|
|
14092
|
-
pm: {
|
|
14093
|
-
...PM_ENTITIES,
|
|
14094
|
-
project: { list: {}, add: {}, create: {}, use: {} },
|
|
14095
|
-
handoff: {},
|
|
14096
|
-
next: {},
|
|
14097
|
-
watch: {}
|
|
14098
|
-
},
|
|
14099
|
-
provision: {},
|
|
14100
|
-
runbook: {
|
|
14101
|
-
ask: {},
|
|
14102
|
-
search: {},
|
|
14103
|
-
impact: {},
|
|
14104
|
-
list: {},
|
|
14105
|
-
get: {},
|
|
14106
|
-
cat: {},
|
|
14107
|
-
new: {},
|
|
14108
|
-
edit: {},
|
|
14109
|
-
comment: {},
|
|
14110
|
-
import: {},
|
|
14111
|
-
visibility: {},
|
|
14112
|
-
publish: {},
|
|
14113
|
-
archive: {},
|
|
14114
|
-
history: {},
|
|
14115
|
-
revert: {},
|
|
14116
|
-
rm: {},
|
|
14117
|
-
lint: {}
|
|
14118
|
-
},
|
|
14119
|
-
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
14120
|
-
security: {
|
|
14121
|
-
plan: {},
|
|
14122
|
-
sources: {},
|
|
14123
|
-
run: {},
|
|
14124
|
-
status: {},
|
|
14125
|
-
report: {},
|
|
14126
|
-
github: { connect: {}, disconnect: {} }
|
|
14127
|
-
},
|
|
14128
|
-
setup: {},
|
|
14129
|
-
skill: { install: {} },
|
|
14130
|
-
smoke: {},
|
|
14131
|
-
version: {},
|
|
14132
|
-
whoami: {}
|
|
14133
|
-
};
|
|
14134
|
-
function acceptedAfter(path) {
|
|
14135
|
-
let node = COMMAND_SURFACE;
|
|
14136
|
-
for (const word of path) {
|
|
14137
|
-
node = node?.[word];
|
|
14138
|
-
if (!node) return [];
|
|
14139
|
-
}
|
|
14140
|
-
return Object.keys(node).sort();
|
|
14141
|
-
}
|
|
14142
|
-
function validateInvocation(words2) {
|
|
14143
|
-
let node = COMMAND_SURFACE;
|
|
14144
|
-
const walked = [];
|
|
14145
|
-
for (const word of words2) {
|
|
14146
|
-
if (Object.keys(node).length === 0) return null;
|
|
14147
|
-
const next = node[word];
|
|
14148
|
-
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
14149
|
-
walked.push(word);
|
|
14150
|
-
node = next;
|
|
14151
|
-
}
|
|
14152
|
-
return null;
|
|
14153
|
-
}
|
|
14154
|
-
function describeProblem(problem) {
|
|
14155
|
-
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
14156
|
-
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
14157
|
-
}
|
|
14158
|
-
function invocationPath(words2) {
|
|
14159
|
-
let node = COMMAND_SURFACE;
|
|
14160
|
-
const path = [];
|
|
14161
|
-
for (const word of words2) {
|
|
14162
|
-
const next = node[word];
|
|
14163
|
-
if (!next) break;
|
|
14164
|
-
path.push(word);
|
|
14165
|
-
node = next;
|
|
14166
|
-
if (Object.keys(node).length === 0) break;
|
|
14167
|
-
}
|
|
14168
|
-
return path;
|
|
14169
|
-
}
|
|
14170
|
-
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
14171
|
-
const paths = [];
|
|
14172
|
-
for (const [word, child] of Object.entries(node)) {
|
|
14173
|
-
const path = [...prefix, word];
|
|
14174
|
-
paths.push(path);
|
|
14175
|
-
paths.push(...surfacePaths(child, path));
|
|
14176
|
-
}
|
|
14177
|
-
return paths;
|
|
14178
|
-
}
|
|
14179
|
-
|
|
14180
|
-
// src/record.ts
|
|
14181
14300
|
function recordInvocation(parsed) {
|
|
14182
14301
|
const file = import_node_process18.default.env.ODLA_CLI_RECORD;
|
|
14183
14302
|
if (!file) return;
|
|
@@ -14232,10 +14351,10 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
14232
14351
|
var UNITS = { d: DAY_MS, w: 7 * DAY_MS, y: 365 * DAY_MS };
|
|
14233
14352
|
function parseDeviceTtl(raw) {
|
|
14234
14353
|
if (raw === void 0 || raw === true) return void 0;
|
|
14235
|
-
const
|
|
14236
|
-
if (!
|
|
14237
|
-
if (
|
|
14238
|
-
const match = /^(\d+)\s*([dwy])$/.exec(
|
|
14354
|
+
const text4 = String(raw).trim().toLowerCase();
|
|
14355
|
+
if (!text4) return void 0;
|
|
14356
|
+
if (text4 === "forever" || text4 === "never") return 100 * 365 * DAY_MS;
|
|
14357
|
+
const match = /^(\d+)\s*([dwy])$/.exec(text4);
|
|
14239
14358
|
if (!match) {
|
|
14240
14359
|
throw new Error(
|
|
14241
14360
|
`--device-ttl expects a duration like 30d, 6w, 2y, or "forever" (got "${raw}")`
|
|
@@ -14266,6 +14385,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14266
14385
|
"wait"
|
|
14267
14386
|
], 3);
|
|
14268
14387
|
const action2 = parsed.positionals[1] ?? "";
|
|
14388
|
+
if (!acceptedAfter(["device"]).includes(action2)) rejectWord(["device"], action2);
|
|
14269
14389
|
const out = deps.stdout ?? console;
|
|
14270
14390
|
const doFetch = deps.fetch ?? fetch;
|
|
14271
14391
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
@@ -14273,7 +14393,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14273
14393
|
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
14274
14394
|
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
14275
14395
|
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
14276
|
-
|
|
14396
|
+
rejectWord(["device"], action2);
|
|
14277
14397
|
}
|
|
14278
14398
|
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14279
14399
|
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
@@ -14617,12 +14737,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
14617
14737
|
// src/runbook-import.ts
|
|
14618
14738
|
var import_node_fs21 = require("fs");
|
|
14619
14739
|
var import_node_path19 = require("path");
|
|
14620
|
-
function parseRunbook(
|
|
14621
|
-
let rest =
|
|
14740
|
+
function parseRunbook(text4, slug) {
|
|
14741
|
+
let rest = text4;
|
|
14622
14742
|
const meta = {};
|
|
14623
|
-
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(
|
|
14743
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text4);
|
|
14624
14744
|
if (fm) {
|
|
14625
|
-
rest =
|
|
14745
|
+
rest = text4.slice(fm[0].length);
|
|
14626
14746
|
for (const line2 of fm[1].split(/\r?\n/)) {
|
|
14627
14747
|
const pair = /^(\w+)\s*:\s*(.+)$/.exec(line2.trim());
|
|
14628
14748
|
if (!pair) continue;
|
|
@@ -15220,6 +15340,7 @@ async function buildContext3(parsed, deps, action2) {
|
|
|
15220
15340
|
async function runbookCommand(parsed, deps = {}) {
|
|
15221
15341
|
const action2 = parsed.positionals[1] ?? "list";
|
|
15222
15342
|
assertArgs(parsed, ALLOWED2, action2 === "ask" || action2 === "search" ? 64 : 4);
|
|
15343
|
+
if (!acceptedAfter(["runbook"]).includes(action2)) rejectWord(["runbook"], action2);
|
|
15223
15344
|
const ctx = await buildContext3(parsed, deps, action2);
|
|
15224
15345
|
const slug = parsed.positionals[2];
|
|
15225
15346
|
switch (action2) {
|
|
@@ -15309,7 +15430,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15309
15430
|
case "rm":
|
|
15310
15431
|
return runbookRemove(ctx, requireSlug(slug, "rm"));
|
|
15311
15432
|
default:
|
|
15312
|
-
|
|
15433
|
+
rejectWord(["runbook"], action2);
|
|
15313
15434
|
}
|
|
15314
15435
|
}
|
|
15315
15436
|
|
|
@@ -15923,9 +16044,7 @@ async function securityCommand(parsed, dependencies) {
|
|
|
15923
16044
|
else printHostedReport(context.stdout, report5);
|
|
15924
16045
|
return;
|
|
15925
16046
|
}
|
|
15926
|
-
if (sub !== "run")
|
|
15927
|
-
throw new Error('unknown security command. Try "odla-ai security plan", "security sources", or "security run".');
|
|
15928
|
-
}
|
|
16047
|
+
if (sub !== "run") rejectWord(["security"], sub);
|
|
15929
16048
|
const sourceId = stringOpt(parsed.options.source);
|
|
15930
16049
|
if (sourceId) await runSourceSecurityCommand(parsed, dependencies, sourceId);
|
|
15931
16050
|
else await runLocalSecurityCommand(parsed, dependencies);
|
|
@@ -15942,9 +16061,7 @@ async function githubSecurityCommand(parsed, dependencies) {
|
|
|
15942
16061
|
stringOpt(parsed.options.env)
|
|
15943
16062
|
);
|
|
15944
16063
|
}
|
|
15945
|
-
if (action2 !== "connect")
|
|
15946
|
-
throw new Error('unknown security github command. Try "odla-ai security github connect".');
|
|
15947
|
-
}
|
|
16064
|
+
if (action2 !== "connect") rejectWord(["security", "github"], action2);
|
|
15948
16065
|
assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
|
|
15949
16066
|
await requireStudioHuman(
|
|
15950
16067
|
stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -16098,6 +16215,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16098
16215
|
}
|
|
16099
16216
|
if (command === "bug") {
|
|
16100
16217
|
const action2 = parsed.positionals[1] ?? "list";
|
|
16218
|
+
if (!acceptedAfter(["bug"]).includes(action2)) rejectWord(["bug"], action2);
|
|
16101
16219
|
const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
16102
16220
|
await pmCommand({
|
|
16103
16221
|
...parsed,
|
|
@@ -16126,7 +16244,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16126
16244
|
return;
|
|
16127
16245
|
}
|
|
16128
16246
|
if (await projectCommand(command, parsed, runtime)) return;
|
|
16129
|
-
|
|
16247
|
+
rejectWord([], command);
|
|
16130
16248
|
}
|
|
16131
16249
|
async function provisionCommand(parsed, dependencies) {
|
|
16132
16250
|
assertArgs(parsed, [
|
|
@@ -16170,7 +16288,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
16170
16288
|
async function calendarCommand(parsed, dependencies) {
|
|
16171
16289
|
const sub = parsed.positionals[1];
|
|
16172
16290
|
if (sub !== "status" && sub !== "calendars" && sub !== "connect" && sub !== "disconnect") {
|
|
16173
|
-
|
|
16291
|
+
rejectWord(["calendar"], sub);
|
|
16174
16292
|
}
|
|
16175
16293
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open", "yes"], 2);
|
|
16176
16294
|
if (sub !== "status" && sub !== "calendars" && parsed.options.json !== void 0) throw new Error(`--json is supported only by calendar status/calendars`);
|