automata-cli 0.11.0 → 0.12.0-develop.554
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +99 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2633,7 +2633,7 @@ function addLabel(target, name, description) {
|
|
|
2633
2633
|
const edit = target.kind === "pr" ? "pr" : "issue";
|
|
2634
2634
|
const args = [edit, "edit", String(target.number), "--add-label", name];
|
|
2635
2635
|
let result = run4("gh", args);
|
|
2636
|
-
if (result.status !== 0 && isMissingLabelError(result.stderr)) {
|
|
2636
|
+
if (result.status !== 0 && isMissingLabelError(result.stderr, name)) {
|
|
2637
2637
|
const created = run4("gh", ["label", "create", name, "--description", description]);
|
|
2638
2638
|
if (created.status !== 0 && !/already exists/i.test(created.stderr)) {
|
|
2639
2639
|
throw new Error(created.stderr.trim() || `Failed to create the label ${name}.`);
|
|
@@ -2732,8 +2732,11 @@ var MISSING_LABEL_PATTERNS = [
|
|
|
2732
2732
|
/could not resolve to a label/i,
|
|
2733
2733
|
/\blabels?\b[^\n]*\b(?:not found|does not exist)\b/i
|
|
2734
2734
|
];
|
|
2735
|
-
function isMissingLabelError(stderr) {
|
|
2736
|
-
|
|
2735
|
+
function isMissingLabelError(stderr, name) {
|
|
2736
|
+
if (MISSING_LABEL_PATTERNS.some((pattern) => pattern.test(stderr))) {
|
|
2737
|
+
return true;
|
|
2738
|
+
}
|
|
2739
|
+
return name !== void 0 && stderr.includes(`'${name}' not found`);
|
|
2737
2740
|
}
|
|
2738
2741
|
function createDraftPullRequest(input) {
|
|
2739
2742
|
const base = [
|
|
@@ -2752,7 +2755,7 @@ function createDraftPullRequest(input) {
|
|
|
2752
2755
|
if (input.label !== void 0 && input.label.length > 0) {
|
|
2753
2756
|
const labelled = run4("gh", [...base, "--label", input.label]);
|
|
2754
2757
|
if (labelled.status === 0) return parseCreatedPrUrl(labelled.stdout, input.head);
|
|
2755
|
-
if (!isMissingLabelError(labelled.stderr)) {
|
|
2758
|
+
if (!isMissingLabelError(labelled.stderr, input.label)) {
|
|
2756
2759
|
throw new Error(
|
|
2757
2760
|
labelled.stderr.trim() || `Failed to open a draft pull request for ${input.head}.`
|
|
2758
2761
|
);
|
|
@@ -2966,7 +2969,7 @@ function runClaude(prompt, options = {}) {
|
|
|
2966
2969
|
if (options.printSteps !== true) return;
|
|
2967
2970
|
try {
|
|
2968
2971
|
const event = JSON.parse(line);
|
|
2969
|
-
formatEvent(event, turnCount);
|
|
2972
|
+
formatEvent(event, turnCount, options.echoResult !== false);
|
|
2970
2973
|
if (event["type"] === "assistant") turnCount++;
|
|
2971
2974
|
} catch {
|
|
2972
2975
|
}
|
|
@@ -3056,7 +3059,7 @@ function formatContentBlock(block) {
|
|
|
3056
3059
|
}
|
|
3057
3060
|
return null;
|
|
3058
3061
|
}
|
|
3059
|
-
function formatResultEvent(event) {
|
|
3062
|
+
function formatResultEvent(event, echoResult) {
|
|
3060
3063
|
const result = event["result"];
|
|
3061
3064
|
const cost = event["cost_usd"];
|
|
3062
3065
|
const duration = event["duration_ms"];
|
|
@@ -3070,16 +3073,16 @@ function formatResultEvent(event) {
|
|
|
3070
3073
|
process.stderr.write(` [info] ${parts.join(" | ")}
|
|
3071
3074
|
`);
|
|
3072
3075
|
}
|
|
3073
|
-
if (result) {
|
|
3076
|
+
if (result && echoResult) {
|
|
3074
3077
|
process.stdout.write(result + "\n");
|
|
3075
3078
|
}
|
|
3076
3079
|
}
|
|
3077
|
-
function formatEvent(event, turnCount) {
|
|
3080
|
+
function formatEvent(event, turnCount, echoResult = true) {
|
|
3078
3081
|
const type = event["type"];
|
|
3079
3082
|
if (type === "assistant") {
|
|
3080
3083
|
formatAssistantEvent(event, turnCount);
|
|
3081
3084
|
} else if (type === "result") {
|
|
3082
|
-
formatResultEvent(event);
|
|
3085
|
+
formatResultEvent(event, echoResult);
|
|
3083
3086
|
}
|
|
3084
3087
|
}
|
|
3085
3088
|
function asText(value, fallback) {
|
|
@@ -8892,6 +8895,48 @@ function withoutWatched(list, id) {
|
|
|
8892
8895
|
return list.filter((entry) => entry !== id);
|
|
8893
8896
|
}
|
|
8894
8897
|
|
|
8898
|
+
// src/conductor/permissionDenials.ts
|
|
8899
|
+
var MAX_TARGET_LENGTH = 300;
|
|
8900
|
+
function targetOf(input) {
|
|
8901
|
+
if (typeof input !== "object" || input === null) return "";
|
|
8902
|
+
const record = input;
|
|
8903
|
+
for (const key2 of ["command", "file_path", "path", "pattern"]) {
|
|
8904
|
+
const value = record[key2];
|
|
8905
|
+
if (typeof value === "string") return value;
|
|
8906
|
+
}
|
|
8907
|
+
return JSON.stringify(input);
|
|
8908
|
+
}
|
|
8909
|
+
function denialOf(entry) {
|
|
8910
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
8911
|
+
const record = entry;
|
|
8912
|
+
const tool = record["tool_name"];
|
|
8913
|
+
if (typeof tool !== "string") return null;
|
|
8914
|
+
return { tool, target: targetOf(record["tool_input"]) };
|
|
8915
|
+
}
|
|
8916
|
+
function parsePermissionDenials(stdout) {
|
|
8917
|
+
const denials = [];
|
|
8918
|
+
for (const line of stdout.split("\n")) {
|
|
8919
|
+
if (!line.trim().startsWith("{")) continue;
|
|
8920
|
+
try {
|
|
8921
|
+
const event = JSON.parse(line);
|
|
8922
|
+
const listed = event["type"] === "result" ? event["permission_denials"] : void 0;
|
|
8923
|
+
if (!Array.isArray(listed)) continue;
|
|
8924
|
+
for (const entry of listed) {
|
|
8925
|
+
const denial = denialOf(entry);
|
|
8926
|
+
if (denial !== null) denials.push(denial);
|
|
8927
|
+
}
|
|
8928
|
+
} catch {
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
return denials;
|
|
8932
|
+
}
|
|
8933
|
+
function describePermissionDenials(denials) {
|
|
8934
|
+
return denials.map((denial) => {
|
|
8935
|
+
const flat = `${denial.tool} ${denial.target}`.replace(/[\u0000-\u001f\u007f-\u009f]+/g, " ").replace(/\s+/g, " ").trim();
|
|
8936
|
+
return flat.length <= MAX_TARGET_LENGTH ? flat : `${flat.slice(0, MAX_TARGET_LENGTH)}\u2026`;
|
|
8937
|
+
});
|
|
8938
|
+
}
|
|
8939
|
+
|
|
8895
8940
|
// src/commands/conductor.ts
|
|
8896
8941
|
function fail2(message) {
|
|
8897
8942
|
process.stderr.write(`Error: ${message}
|
|
@@ -8956,7 +9001,29 @@ function readTargetMessages(target) {
|
|
|
8956
9001
|
}
|
|
8957
9002
|
function runModel(prompt, execution, replyTo, sink2) {
|
|
8958
9003
|
const { owner, repo } = getRepoSlug();
|
|
8959
|
-
return runClaude(prompt, { model: execution.model, effort: execution.effort, readOnly: true, replyTo, ghRepo: `${owner}/${repo}`, sink: sink2 });
|
|
9004
|
+
return runClaude(prompt, { model: execution.model, effort: execution.effort, readOnly: true, replyTo, ghRepo: `${owner}/${repo}`, sink: sink2, printSteps: verboseRun, echoResult: false });
|
|
9005
|
+
}
|
|
9006
|
+
var verboseRun = false;
|
|
9007
|
+
function reportDenials(stdout, label2) {
|
|
9008
|
+
if (!verboseRun || stdout.length === 0) return;
|
|
9009
|
+
const lines = describePermissionDenials(parsePermissionDenials(stdout));
|
|
9010
|
+
if (lines.length === 0) {
|
|
9011
|
+
process.stderr.write(`Conductor: the run for ${label2} reported no denied tool call.
|
|
9012
|
+
`);
|
|
9013
|
+
return;
|
|
9014
|
+
}
|
|
9015
|
+
process.stderr.write(`Conductor: the run for ${label2} was denied ${String(lines.length)} tool call(s):
|
|
9016
|
+
`);
|
|
9017
|
+
for (const line of lines) process.stderr.write(` - ${line}
|
|
9018
|
+
`);
|
|
9019
|
+
}
|
|
9020
|
+
function reportCommandTrace() {
|
|
9021
|
+
const trace = takeCommandTrace();
|
|
9022
|
+
if (trace === null) return;
|
|
9023
|
+
process.stderr.write(`Commands (${String(trace.length)})
|
|
9024
|
+
`);
|
|
9025
|
+
for (const traced of trace) process.stderr.write(` ${describeTracedCommand(traced)}
|
|
9026
|
+
`);
|
|
8960
9027
|
}
|
|
8961
9028
|
function captureStdout() {
|
|
8962
9029
|
let stdout = "";
|
|
@@ -8971,8 +9038,12 @@ function captureStdout() {
|
|
|
8971
9038
|
async function runModelForText(prompt, execution) {
|
|
8972
9039
|
const { owner, repo } = getRepoSlug();
|
|
8973
9040
|
const captured = captureStdout();
|
|
8974
|
-
const options = { model: execution.model, effort: execution.effort, readOnly: true, ghRepo: `${owner}/${repo}`, sink: captured.sink };
|
|
8975
|
-
|
|
9041
|
+
const options = { model: execution.model, effort: execution.effort, readOnly: true, ghRepo: `${owner}/${repo}`, sink: captured.sink, printSteps: verboseRun, echoResult: false };
|
|
9042
|
+
try {
|
|
9043
|
+
await runClaude(prompt, options);
|
|
9044
|
+
} finally {
|
|
9045
|
+
reportDenials(captured.text(), "the dry run");
|
|
9046
|
+
}
|
|
8976
9047
|
return extractDryRunReply(execution.executor, captured.text());
|
|
8977
9048
|
}
|
|
8978
9049
|
function coveredOf(watch, linkMap) {
|
|
@@ -9052,6 +9123,7 @@ async function conductItem(id, config, login2, participants, execution, dryRun,
|
|
|
9052
9123
|
read: () => readTargetMessages(replyTo),
|
|
9053
9124
|
run: () => runModel(prompt, execution, replyTo, captured.sink)
|
|
9054
9125
|
});
|
|
9126
|
+
reportDenials(captured.text(), label2);
|
|
9055
9127
|
const needsHuman = parseNeedsHuman(extractDryRunReply(execution.executor, captured.text()) ?? "");
|
|
9056
9128
|
if (needsHuman !== null && outcome.kind !== "run-failed" && outcome.kind !== "unverified") {
|
|
9057
9129
|
return blockItem(item, label2, needsHuman);
|
|
@@ -9300,6 +9372,17 @@ function reportHeldLock(held, suspect, staleMinutes) {
|
|
|
9300
9372
|
return 2;
|
|
9301
9373
|
}
|
|
9302
9374
|
async function runConductor(options = {}) {
|
|
9375
|
+
verboseRun = options.verbose === true;
|
|
9376
|
+
if (verboseRun) startCommandTrace();
|
|
9377
|
+
try {
|
|
9378
|
+
return await runConductorTick(options);
|
|
9379
|
+
} finally {
|
|
9380
|
+
reportCommandTrace();
|
|
9381
|
+
verboseRun = false;
|
|
9382
|
+
stopCommandTrace();
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9385
|
+
async function runConductorTick(options) {
|
|
9303
9386
|
if (options.check === true && options.dryRun === true) {
|
|
9304
9387
|
return fail2("--check and --dry-run cannot be used together.");
|
|
9305
9388
|
}
|
|
@@ -9340,7 +9423,10 @@ async function runConductor(options = {}) {
|
|
|
9340
9423
|
}
|
|
9341
9424
|
var conductorCommand = new Command7("conductor").description(
|
|
9342
9425
|
"Run one conductor tick as an allowed (human) account: verify the identity, take the conductor's own run lock, then act"
|
|
9343
|
-
).option("--check", "Report what a tick would do, including pending prunes, and change nothing; exit 1 on a problem").option("--dry-run", "Run the model for each owed reply and print it instead of posting; no lock, no prune").
|
|
9426
|
+
).option("--check", "Report what a tick would do, including pending prunes, and change nothing; exit 1 on a problem").option("--dry-run", "Run the model for each owed reply and print it instead of posting; no lock, no prune").option(
|
|
9427
|
+
"--verbose",
|
|
9428
|
+
"Trace every `git` and `gh` call with its duration and exit code, show the model's steps, and list the tool calls the run was denied"
|
|
9429
|
+
).action(async (options) => {
|
|
9344
9430
|
exitWith(await runConductor(options));
|
|
9345
9431
|
});
|
|
9346
9432
|
conductorCommand.command("add <id>").description("Watch an issue or pull request and make do-work pick it up (an issue also follows its linked PR)").action((id) => {
|