@rogatio/cli 5.17.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 +73 -25
- package/dist/node/index.js +181 -30
- package/package.json +1 -1
package/dist/editor/index.js
CHANGED
|
@@ -1844,9 +1844,6 @@ var CREATABLE_RULE_FIELDS = /* @__PURE__ */ new Set([
|
|
|
1844
1844
|
...ACTION_PAYLOAD_FIELDS,
|
|
1845
1845
|
"redactSensitiveInLogs"
|
|
1846
1846
|
]);
|
|
1847
|
-
function isBodyRuleType(type) {
|
|
1848
|
-
return type === "request-body" || type === "response-body";
|
|
1849
|
-
}
|
|
1850
1847
|
function setValueAtPath(root, path, value) {
|
|
1851
1848
|
const segments = decodePointer(path);
|
|
1852
1849
|
if (!segments || segments.length === 0) return false;
|
|
@@ -2003,6 +2000,7 @@ function ownString(record, ...keys) {
|
|
|
2003
2000
|
}
|
|
2004
2001
|
return void 0;
|
|
2005
2002
|
}
|
|
2003
|
+
var RULE_PATH_PATTERN = /^\/groups\/(0|[1-9][0-9]*)\/rules\/(0|[1-9][0-9]*)(?:\/|$)/;
|
|
2006
2004
|
function applyRuleProposalAction(rule, ruleProposal) {
|
|
2007
2005
|
const action = ruleProposal.action;
|
|
2008
2006
|
switch (ruleProposal.kind) {
|
|
@@ -2126,6 +2124,7 @@ var EditorControllerImpl = class {
|
|
|
2126
2124
|
testRequestId = 0;
|
|
2127
2125
|
aiAssistPanel = null;
|
|
2128
2126
|
aiAssistInFlight = false;
|
|
2127
|
+
aiRepairTargets = [];
|
|
2129
2128
|
constructor(options, initial, extensions) {
|
|
2130
2129
|
this.root = options.root;
|
|
2131
2130
|
this.document = options.root.ownerDocument;
|
|
@@ -2975,6 +2974,8 @@ var EditorControllerImpl = class {
|
|
|
2975
2974
|
this.render();
|
|
2976
2975
|
}
|
|
2977
2976
|
applyAIProposal(proposal) {
|
|
2977
|
+
const repairs = [...this.aiRepairTargets];
|
|
2978
|
+
this.aiRepairTargets = [];
|
|
2978
2979
|
for (const ruleProposal of proposal.rules) {
|
|
2979
2980
|
let group = this.groupById(ruleProposal.groupId);
|
|
2980
2981
|
if (!group) {
|
|
@@ -2988,7 +2989,19 @@ var EditorControllerImpl = class {
|
|
|
2988
2989
|
group = this.groupById(groupId);
|
|
2989
2990
|
}
|
|
2990
2991
|
if (!group) return;
|
|
2991
|
-
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
|
+
}
|
|
2992
3005
|
const rule = {
|
|
2993
3006
|
id: ruleId,
|
|
2994
3007
|
name: ruleProposal.name,
|
|
@@ -3001,20 +3014,56 @@ var EditorControllerImpl = class {
|
|
|
3001
3014
|
rule.method = ruleProposal.method;
|
|
3002
3015
|
}
|
|
3003
3016
|
applyRuleProposalAction(rule, ruleProposal);
|
|
3004
|
-
|
|
3017
|
+
if (repairIndex >= 0) {
|
|
3018
|
+
rules[repairIndex] = rule;
|
|
3019
|
+
} else {
|
|
3020
|
+
rules.push(rule);
|
|
3021
|
+
}
|
|
3005
3022
|
this.markChanged();
|
|
3006
3023
|
const groupIdStr = String(group.id);
|
|
3007
3024
|
this.focusRequest = pointer(
|
|
3008
3025
|
"groups",
|
|
3009
3026
|
this.groupIndex(groupIdStr),
|
|
3010
3027
|
"rules",
|
|
3011
|
-
|
|
3028
|
+
repairIndex >= 0 ? repairIndex : rules.length - 1,
|
|
3012
3029
|
"name"
|
|
3013
3030
|
);
|
|
3014
3031
|
}
|
|
3015
3032
|
this.statusMessage = `Applied ${proposal.rules.length} rule${proposal.rules.length === 1 ? "" : "s"} from AI.`;
|
|
3016
3033
|
this.render();
|
|
3017
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
|
+
}
|
|
3018
3067
|
async runAIAssist(prompt) {
|
|
3019
3068
|
const handler = this.options.aiAssist;
|
|
3020
3069
|
const panel = this.aiAssistPanel;
|
|
@@ -3033,6 +3082,7 @@ var EditorControllerImpl = class {
|
|
|
3033
3082
|
diagnostics: diagnostics.length > 0 ? diagnostics : void 0
|
|
3034
3083
|
}
|
|
3035
3084
|
};
|
|
3085
|
+
this.aiRepairTargets = request.kind === "fix" && diagnostics.length > 0 ? this.repairTargetsFrom(diagnostics) : [];
|
|
3036
3086
|
panel.startStreaming("assistant");
|
|
3037
3087
|
try {
|
|
3038
3088
|
const result = handler(request);
|
|
@@ -3937,25 +3987,23 @@ var EditorControllerImpl = class {
|
|
|
3937
3987
|
this.renderField(matcherGrid, "Method", `${rulePath}/method`, method);
|
|
3938
3988
|
matcherFields.append(matcherGrid);
|
|
3939
3989
|
card.append(matcherFields);
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
card.append(redactFieldset);
|
|
3958
|
-
}
|
|
3990
|
+
const redactFieldset = this.document.createElement("fieldset");
|
|
3991
|
+
const redactLegend = this.document.createElement("legend");
|
|
3992
|
+
redactLegend.textContent = "Match logging";
|
|
3993
|
+
redactFieldset.append(redactLegend);
|
|
3994
|
+
const redactGrid = this.document.createElement("div");
|
|
3995
|
+
redactGrid.dataset.editorFields = "true";
|
|
3996
|
+
const redactCheckbox = this.document.createElement("input");
|
|
3997
|
+
redactCheckbox.type = "checkbox";
|
|
3998
|
+
redactCheckbox.checked = rule.redactSensitiveInLogs === true;
|
|
3999
|
+
this.renderField(
|
|
4000
|
+
redactGrid,
|
|
4001
|
+
"Redact sensitive fields in logs",
|
|
4002
|
+
`${rulePath}/redactSensitiveInLogs`,
|
|
4003
|
+
redactCheckbox
|
|
4004
|
+
);
|
|
4005
|
+
redactFieldset.append(redactGrid);
|
|
4006
|
+
card.append(redactFieldset);
|
|
3959
4007
|
if (this.extensions.length > 0) {
|
|
3960
4008
|
const currentType = this.extensions.find(
|
|
3961
4009
|
(e) => e.matches(rule)
|
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