@rogatio/cli 5.18.0 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/index.js +56 -3
- package/dist/node/index.js +181 -30
- package/package.json +1 -1
package/dist/editor/index.js
CHANGED
|
@@ -2000,6 +2000,7 @@ function ownString(record, ...keys) {
|
|
|
2000
2000
|
}
|
|
2001
2001
|
return void 0;
|
|
2002
2002
|
}
|
|
2003
|
+
var RULE_PATH_PATTERN = /^\/groups\/(0|[1-9][0-9]*)\/rules\/(0|[1-9][0-9]*)(?:\/|$)/;
|
|
2003
2004
|
function applyRuleProposalAction(rule, ruleProposal) {
|
|
2004
2005
|
const action = ruleProposal.action;
|
|
2005
2006
|
switch (ruleProposal.kind) {
|
|
@@ -2123,6 +2124,7 @@ var EditorControllerImpl = class {
|
|
|
2123
2124
|
testRequestId = 0;
|
|
2124
2125
|
aiAssistPanel = null;
|
|
2125
2126
|
aiAssistInFlight = false;
|
|
2127
|
+
aiRepairTargets = [];
|
|
2126
2128
|
constructor(options, initial, extensions) {
|
|
2127
2129
|
this.root = options.root;
|
|
2128
2130
|
this.document = options.root.ownerDocument;
|
|
@@ -2972,6 +2974,8 @@ var EditorControllerImpl = class {
|
|
|
2972
2974
|
this.render();
|
|
2973
2975
|
}
|
|
2974
2976
|
applyAIProposal(proposal) {
|
|
2977
|
+
const repairs = [...this.aiRepairTargets];
|
|
2978
|
+
this.aiRepairTargets = [];
|
|
2975
2979
|
for (const ruleProposal of proposal.rules) {
|
|
2976
2980
|
let group = this.groupById(ruleProposal.groupId);
|
|
2977
2981
|
if (!group) {
|
|
@@ -2985,7 +2989,19 @@ var EditorControllerImpl = class {
|
|
|
2985
2989
|
group = this.groupById(groupId);
|
|
2986
2990
|
}
|
|
2987
2991
|
if (!group) return;
|
|
2988
|
-
const
|
|
2992
|
+
const rules = group.rules;
|
|
2993
|
+
const repair = repairs.find(
|
|
2994
|
+
(target) => target.groupId === ruleProposal.groupId && rules.some((existing) => String(existing.id) === target.ruleId)
|
|
2995
|
+
);
|
|
2996
|
+
let repairIndex = -1;
|
|
2997
|
+
let ruleId = this.nextId("rule-ai");
|
|
2998
|
+
if (repair) {
|
|
2999
|
+
repairs.splice(repairs.indexOf(repair), 1);
|
|
3000
|
+
ruleId = repair.ruleId;
|
|
3001
|
+
repairIndex = rules.findIndex(
|
|
3002
|
+
(existing) => String(existing.id) === repair.ruleId
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
2989
3005
|
const rule = {
|
|
2990
3006
|
id: ruleId,
|
|
2991
3007
|
name: ruleProposal.name,
|
|
@@ -2998,20 +3014,56 @@ var EditorControllerImpl = class {
|
|
|
2998
3014
|
rule.method = ruleProposal.method;
|
|
2999
3015
|
}
|
|
3000
3016
|
applyRuleProposalAction(rule, ruleProposal);
|
|
3001
|
-
|
|
3017
|
+
if (repairIndex >= 0) {
|
|
3018
|
+
rules[repairIndex] = rule;
|
|
3019
|
+
} else {
|
|
3020
|
+
rules.push(rule);
|
|
3021
|
+
}
|
|
3002
3022
|
this.markChanged();
|
|
3003
3023
|
const groupIdStr = String(group.id);
|
|
3004
3024
|
this.focusRequest = pointer(
|
|
3005
3025
|
"groups",
|
|
3006
3026
|
this.groupIndex(groupIdStr),
|
|
3007
3027
|
"rules",
|
|
3008
|
-
|
|
3028
|
+
repairIndex >= 0 ? repairIndex : rules.length - 1,
|
|
3009
3029
|
"name"
|
|
3010
3030
|
);
|
|
3011
3031
|
}
|
|
3012
3032
|
this.statusMessage = `Applied ${proposal.rules.length} rule${proposal.rules.length === 1 ? "" : "s"} from AI.`;
|
|
3013
3033
|
this.render();
|
|
3014
3034
|
}
|
|
3035
|
+
/**
|
|
3036
|
+
* Rules carrying validation errors, in stable path order. A fix proposal
|
|
3037
|
+
* repairs these rules in place (keeping their ids) instead of adding new ones.
|
|
3038
|
+
*/
|
|
3039
|
+
repairTargetsFrom(diagnostics) {
|
|
3040
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3041
|
+
const positions = [];
|
|
3042
|
+
for (const diagnostic2 of diagnostics) {
|
|
3043
|
+
const match = RULE_PATH_PATTERN.exec(diagnostic2.path);
|
|
3044
|
+
if (!match) continue;
|
|
3045
|
+
const groupIndex = Number(match[1]);
|
|
3046
|
+
const ruleIndex = Number(match[2]);
|
|
3047
|
+
if (!Number.isSafeInteger(groupIndex) || !Number.isSafeInteger(ruleIndex)) {
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
const key = `${groupIndex}/${ruleIndex}`;
|
|
3051
|
+
if (seen.has(key)) continue;
|
|
3052
|
+
seen.add(key);
|
|
3053
|
+
positions.push({ groupIndex, ruleIndex });
|
|
3054
|
+
}
|
|
3055
|
+
positions.sort(
|
|
3056
|
+
(left, right) => left.groupIndex - right.groupIndex || left.ruleIndex - right.ruleIndex
|
|
3057
|
+
);
|
|
3058
|
+
const targets = [];
|
|
3059
|
+
for (const position of positions) {
|
|
3060
|
+
const group = this.draft.groups[position.groupIndex];
|
|
3061
|
+
const rule = group?.rules[position.ruleIndex];
|
|
3062
|
+
if (!group || !rule) continue;
|
|
3063
|
+
targets.push({ groupId: String(group.id), ruleId: String(rule.id) });
|
|
3064
|
+
}
|
|
3065
|
+
return targets;
|
|
3066
|
+
}
|
|
3015
3067
|
async runAIAssist(prompt) {
|
|
3016
3068
|
const handler = this.options.aiAssist;
|
|
3017
3069
|
const panel = this.aiAssistPanel;
|
|
@@ -3030,6 +3082,7 @@ var EditorControllerImpl = class {
|
|
|
3030
3082
|
diagnostics: diagnostics.length > 0 ? diagnostics : void 0
|
|
3031
3083
|
}
|
|
3032
3084
|
};
|
|
3085
|
+
this.aiRepairTargets = request.kind === "fix" && diagnostics.length > 0 ? this.repairTargetsFrom(diagnostics) : [];
|
|
3033
3086
|
panel.startStreaming("assistant");
|
|
3034
3087
|
try {
|
|
3035
3088
|
const result = handler(request);
|
package/dist/node/index.js
CHANGED
|
@@ -20878,6 +20878,115 @@ function mergeProposalIntoProject(project, proposal) {
|
|
|
20878
20878
|
}
|
|
20879
20879
|
return { ...project, groups };
|
|
20880
20880
|
}
|
|
20881
|
+
var RULE_PATH_PATTERN = /^\/groups\/(0|[1-9][0-9]*)\/rules\/(0|[1-9][0-9]*)(?:\/|$)/;
|
|
20882
|
+
function repairTargetsFromDiagnostics(diagnostics) {
|
|
20883
|
+
if (!Array.isArray(diagnostics)) return [];
|
|
20884
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20885
|
+
const targets = [];
|
|
20886
|
+
for (const diagnostic of diagnostics) {
|
|
20887
|
+
if (!isRecord(diagnostic) || typeof diagnostic.path !== "string") continue;
|
|
20888
|
+
const match = RULE_PATH_PATTERN.exec(diagnostic.path);
|
|
20889
|
+
if (!match) continue;
|
|
20890
|
+
const groupIndex = Number(match[1]);
|
|
20891
|
+
const ruleIndex = Number(match[2]);
|
|
20892
|
+
if (!Number.isSafeInteger(groupIndex) || !Number.isSafeInteger(ruleIndex)) {
|
|
20893
|
+
continue;
|
|
20894
|
+
}
|
|
20895
|
+
const key = `${groupIndex}/${ruleIndex}`;
|
|
20896
|
+
if (seen.has(key)) continue;
|
|
20897
|
+
seen.add(key);
|
|
20898
|
+
targets.push({ groupIndex, ruleIndex });
|
|
20899
|
+
}
|
|
20900
|
+
targets.sort(
|
|
20901
|
+
(left, right) => left.groupIndex - right.groupIndex || left.ruleIndex - right.ruleIndex
|
|
20902
|
+
);
|
|
20903
|
+
return targets;
|
|
20904
|
+
}
|
|
20905
|
+
var RULE_KINDS = /* @__PURE__ */ new Set([
|
|
20906
|
+
"redirect",
|
|
20907
|
+
"query",
|
|
20908
|
+
"header",
|
|
20909
|
+
"response-body",
|
|
20910
|
+
"request-body"
|
|
20911
|
+
]);
|
|
20912
|
+
function stringArray(value) {
|
|
20913
|
+
if (!Array.isArray(value)) return void 0;
|
|
20914
|
+
const items = value.filter(
|
|
20915
|
+
(item) => typeof item === "string"
|
|
20916
|
+
);
|
|
20917
|
+
return items.length === value.length ? items : void 0;
|
|
20918
|
+
}
|
|
20919
|
+
function parseProposalRule(value) {
|
|
20920
|
+
if (!isRecord(value)) return null;
|
|
20921
|
+
const kind = value.kind;
|
|
20922
|
+
if (typeof kind !== "string" || !RULE_KINDS.has(kind) || typeof value.groupId !== "string" || typeof value.name !== "string" || typeof value.urlRegex !== "string" || !Object.hasOwn(value, "action")) {
|
|
20923
|
+
return null;
|
|
20924
|
+
}
|
|
20925
|
+
return {
|
|
20926
|
+
kind,
|
|
20927
|
+
groupId: value.groupId,
|
|
20928
|
+
name: value.name,
|
|
20929
|
+
urlRegex: value.urlRegex,
|
|
20930
|
+
origins: stringArray(value.origins),
|
|
20931
|
+
resourceTypes: stringArray(value.resourceTypes),
|
|
20932
|
+
priority: typeof value.priority === "number" ? value.priority : void 0,
|
|
20933
|
+
method: typeof value.method === "string" ? value.method : void 0,
|
|
20934
|
+
action: value.action
|
|
20935
|
+
};
|
|
20936
|
+
}
|
|
20937
|
+
function repairProposalIntoProject(project, diagnostics, proposal) {
|
|
20938
|
+
const groups = Array.isArray(project.groups) ? project.groups.map((group) => {
|
|
20939
|
+
if (!isRecord(group)) {
|
|
20940
|
+
return { id: "invalid", name: "invalid", origins: [], rules: [] };
|
|
20941
|
+
}
|
|
20942
|
+
return {
|
|
20943
|
+
...group,
|
|
20944
|
+
rules: Array.isArray(group.rules) ? [...group.rules] : []
|
|
20945
|
+
};
|
|
20946
|
+
}) : [];
|
|
20947
|
+
const queues = /* @__PURE__ */ new Map();
|
|
20948
|
+
for (const target of repairTargetsFromDiagnostics(diagnostics)) {
|
|
20949
|
+
const group = groups[target.groupIndex];
|
|
20950
|
+
if (!group) continue;
|
|
20951
|
+
const rules = group.rules;
|
|
20952
|
+
if (target.ruleIndex >= rules.length) continue;
|
|
20953
|
+
if (!isRecord(rules[target.ruleIndex])) continue;
|
|
20954
|
+
const queue = queues.get(target.groupIndex) ?? [];
|
|
20955
|
+
queue.push(target.ruleIndex);
|
|
20956
|
+
queues.set(target.groupIndex, queue);
|
|
20957
|
+
}
|
|
20958
|
+
let seq = 0;
|
|
20959
|
+
const newId = () => `ai-${Date.now()}-${seq++}`;
|
|
20960
|
+
const proposed = Array.isArray(proposal.rules) ? proposal.rules : [];
|
|
20961
|
+
for (const entry of proposed) {
|
|
20962
|
+
const ruleProposal = parseProposalRule(entry);
|
|
20963
|
+
if (!ruleProposal) continue;
|
|
20964
|
+
const groupIndex = groups.findIndex(
|
|
20965
|
+
(candidate) => candidate.id === ruleProposal.groupId
|
|
20966
|
+
);
|
|
20967
|
+
const queue = groupIndex >= 0 ? queues.get(groupIndex) : void 0;
|
|
20968
|
+
const targetIndex = queue?.shift();
|
|
20969
|
+
if (groupIndex >= 0 && targetIndex !== void 0) {
|
|
20970
|
+
const rules = groups[groupIndex].rules;
|
|
20971
|
+
const existing = rules[targetIndex];
|
|
20972
|
+
const keptId = typeof existing.id === "string" ? existing.id : newId();
|
|
20973
|
+
rules[targetIndex] = ruleFromProposal(ruleProposal, keptId);
|
|
20974
|
+
continue;
|
|
20975
|
+
}
|
|
20976
|
+
let group = groupIndex >= 0 ? groups[groupIndex] : void 0;
|
|
20977
|
+
if (!group) {
|
|
20978
|
+
group = {
|
|
20979
|
+
id: ruleProposal.groupId,
|
|
20980
|
+
name: "AI Group",
|
|
20981
|
+
origins: [],
|
|
20982
|
+
rules: []
|
|
20983
|
+
};
|
|
20984
|
+
groups.push(group);
|
|
20985
|
+
}
|
|
20986
|
+
group.rules.push(ruleFromProposal(ruleProposal, newId()));
|
|
20987
|
+
}
|
|
20988
|
+
return { ...project, groups };
|
|
20989
|
+
}
|
|
20881
20990
|
function parseProposal(content) {
|
|
20882
20991
|
try {
|
|
20883
20992
|
const parsed = JSON.parse(content);
|
|
@@ -20943,6 +21052,11 @@ async function runStream(client, options) {
|
|
|
20943
21052
|
async function runAIAssist(request, config, validate, dryRun, client) {
|
|
20944
21053
|
const aiClient = client ?? createAIClient(config);
|
|
20945
21054
|
const systemPrompt = buildSystemPrompt(request.context.project);
|
|
21055
|
+
const merge = (project, proposal) => request.kind === "fix" ? repairProposalIntoProject(
|
|
21056
|
+
project,
|
|
21057
|
+
request.context.diagnostics,
|
|
21058
|
+
proposal
|
|
21059
|
+
) : mergeProposalIntoProject(project, proposal);
|
|
20946
21060
|
let currentProposal = null;
|
|
20947
21061
|
let attempts = 0;
|
|
20948
21062
|
while (attempts < MAX_FIX_ATTEMPTS) {
|
|
@@ -20950,9 +21064,7 @@ async function runAIAssist(request, config, validate, dryRun, client) {
|
|
|
20950
21064
|
const isFixAttempt = attempts > 1 || request.kind === "fix";
|
|
20951
21065
|
const validationErrors = (() => {
|
|
20952
21066
|
if (!isFixAttempt || !currentProposal) return [];
|
|
20953
|
-
return validate(
|
|
20954
|
-
mergeProposalIntoProject(request.context.project, currentProposal)
|
|
20955
|
-
);
|
|
21067
|
+
return validate(merge(request.context.project, currentProposal));
|
|
20956
21068
|
})();
|
|
20957
21069
|
const messages = buildMessages(
|
|
20958
21070
|
systemPrompt,
|
|
@@ -20983,10 +21095,7 @@ async function runAIAssist(request, config, validate, dryRun, client) {
|
|
|
20983
21095
|
throw new Error("AI returned invalid proposal format");
|
|
20984
21096
|
}
|
|
20985
21097
|
currentProposal = proposal;
|
|
20986
|
-
const testProject =
|
|
20987
|
-
request.context.project,
|
|
20988
|
-
proposal
|
|
20989
|
-
);
|
|
21098
|
+
const testProject = merge(request.context.project, proposal);
|
|
20990
21099
|
const diagnostics = validate(testProject);
|
|
20991
21100
|
if (diagnostics.length === 0) {
|
|
20992
21101
|
if (dryRun && request.context.dryRunCases && request.context.dryRunCases.length > 0) {
|
|
@@ -23526,36 +23635,78 @@ Options:
|
|
|
23526
23635
|
}
|
|
23527
23636
|
|
|
23528
23637
|
// packages/cli/src/commands/ai.ts
|
|
23638
|
+
var pendingInput = "";
|
|
23639
|
+
function readLine() {
|
|
23640
|
+
return new Promise((resolve9) => {
|
|
23641
|
+
const take = () => {
|
|
23642
|
+
const newline = pendingInput.search(/[\r\n]/u);
|
|
23643
|
+
if (newline === -1) return false;
|
|
23644
|
+
const line = pendingInput.slice(0, newline);
|
|
23645
|
+
pendingInput = pendingInput.slice(newline + 1);
|
|
23646
|
+
resolve9(line);
|
|
23647
|
+
return true;
|
|
23648
|
+
};
|
|
23649
|
+
if (take()) return;
|
|
23650
|
+
const onData = (chunk) => {
|
|
23651
|
+
pendingInput += chunk.toString();
|
|
23652
|
+
if (!take()) return;
|
|
23653
|
+
process.stdin.off("data", onData);
|
|
23654
|
+
process.stdin.pause();
|
|
23655
|
+
};
|
|
23656
|
+
process.stdin.on("data", onData);
|
|
23657
|
+
process.stdin.resume();
|
|
23658
|
+
});
|
|
23659
|
+
}
|
|
23529
23660
|
async function promptInput(prompt) {
|
|
23530
23661
|
process.stdout.write(prompt);
|
|
23531
|
-
|
|
23532
|
-
return chunk.toString().trim();
|
|
23533
|
-
}
|
|
23534
|
-
return "";
|
|
23662
|
+
return (await readLine()).trim();
|
|
23535
23663
|
}
|
|
23536
23664
|
async function promptSecret(prompt) {
|
|
23537
23665
|
process.stdout.write(prompt);
|
|
23538
23666
|
process.stdin.setRawMode(true);
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23667
|
+
try {
|
|
23668
|
+
return await new Promise((resolve9) => {
|
|
23669
|
+
let input = "";
|
|
23670
|
+
let settled = false;
|
|
23671
|
+
const finish = () => {
|
|
23672
|
+
settled = true;
|
|
23673
|
+
process.stdin.off("data", onData);
|
|
23674
|
+
process.stdin.pause();
|
|
23675
|
+
resolve9(input.trim());
|
|
23676
|
+
};
|
|
23677
|
+
const consume = (text) => {
|
|
23678
|
+
if (settled) return;
|
|
23679
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
23680
|
+
const char = text[index];
|
|
23681
|
+
if (char === "\n" || char === "\r") {
|
|
23682
|
+
process.stdout.write("\n");
|
|
23683
|
+
pendingInput = text.slice(index + 1);
|
|
23684
|
+
finish();
|
|
23685
|
+
return;
|
|
23686
|
+
}
|
|
23687
|
+
if (char === "") {
|
|
23688
|
+
process.exit(1);
|
|
23689
|
+
} else if (char === "\x7F" || char === "\b") {
|
|
23690
|
+
if (input.length > 0) {
|
|
23691
|
+
input = input.slice(0, -1);
|
|
23692
|
+
process.stdout.write("\b \b");
|
|
23693
|
+
}
|
|
23694
|
+
} else {
|
|
23695
|
+
input += char;
|
|
23696
|
+
process.stdout.write("*");
|
|
23697
|
+
}
|
|
23698
|
+
}
|
|
23699
|
+
};
|
|
23700
|
+
const onData = (chunk) => consume(chunk.toString());
|
|
23701
|
+
consume(pendingInput);
|
|
23702
|
+
if (settled) return;
|
|
23703
|
+
pendingInput = "";
|
|
23704
|
+
process.stdin.on("data", onData);
|
|
23705
|
+
process.stdin.resume();
|
|
23706
|
+
});
|
|
23707
|
+
} finally {
|
|
23708
|
+
process.stdin.setRawMode(false);
|
|
23556
23709
|
}
|
|
23557
|
-
process.stdin.setRawMode(false);
|
|
23558
|
-
return input.trim();
|
|
23559
23710
|
}
|
|
23560
23711
|
async function aiCommand(args) {
|
|
23561
23712
|
if (args.includes("--help") || args.includes("-h")) {
|
package/package.json
CHANGED