@intellectronica/ruler 0.2.19 → 0.3.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 +190 -31
- package/dist/agents/AbstractAgent.js +82 -0
- package/dist/agents/AgentsMdAgent.js +82 -0
- package/dist/agents/AiderAgent.js +29 -9
- package/dist/agents/AmpAgent.js +2 -44
- package/dist/agents/AugmentCodeAgent.js +10 -12
- package/dist/agents/ClaudeAgent.js +9 -9
- package/dist/agents/ClineAgent.js +3 -9
- package/dist/agents/CodexCliAgent.js +27 -21
- package/dist/agents/CopilotAgent.js +9 -10
- package/dist/agents/CrushAgent.js +6 -0
- package/dist/agents/CursorAgent.js +13 -5
- package/dist/agents/FirebaseAgent.js +2 -8
- package/dist/agents/GeminiCliAgent.js +31 -22
- package/dist/agents/GooseAgent.js +2 -10
- package/dist/agents/JulesAgent.js +3 -44
- package/dist/agents/JunieAgent.js +2 -9
- package/dist/agents/KiloCodeAgent.js +8 -9
- package/dist/agents/KiroAgent.js +50 -0
- package/dist/agents/OpenCodeAgent.js +50 -11
- package/dist/agents/OpenHandsAgent.js +8 -9
- package/dist/agents/QwenCodeAgent.js +83 -0
- package/dist/agents/WarpAgent.js +61 -0
- package/dist/agents/WindsurfAgent.js +9 -10
- package/dist/agents/ZedAgent.js +132 -0
- package/dist/agents/agent-utils.js +37 -0
- package/dist/agents/index.js +53 -0
- package/dist/cli/commands.js +48 -242
- package/dist/cli/handlers.js +176 -0
- package/dist/constants.js +9 -2
- package/dist/core/ConfigLoader.js +1 -1
- package/dist/core/FileSystemUtils.js +51 -4
- package/dist/core/RuleProcessor.js +15 -3
- package/dist/core/UnifiedConfigLoader.js +357 -0
- package/dist/core/UnifiedConfigTypes.js +2 -0
- package/dist/core/agent-selection.js +52 -0
- package/dist/core/apply-engine.js +302 -0
- package/dist/core/config-utils.js +30 -0
- package/dist/core/hash.js +24 -0
- package/dist/core/revert-engine.js +413 -0
- package/dist/lib.js +20 -330
- package/dist/mcp/capabilities.js +51 -0
- package/dist/mcp/propagateOpenCodeMcp.js +30 -31
- package/dist/mcp/propagateOpenHandsMcp.js +101 -17
- package/dist/paths/mcp.js +7 -3
- package/dist/revert.js +96 -481
- package/package.json +2 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadRulerConfiguration = loadRulerConfiguration;
|
|
37
|
+
exports.selectAgentsToRun = selectAgentsToRun;
|
|
38
|
+
exports.applyConfigurationsToAgents = applyConfigurationsToAgents;
|
|
39
|
+
exports.updateGitignore = updateGitignore;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const FileSystemUtils = __importStar(require("./FileSystemUtils"));
|
|
42
|
+
const RuleProcessor_1 = require("./RuleProcessor");
|
|
43
|
+
const ConfigLoader_1 = require("./ConfigLoader");
|
|
44
|
+
const GitignoreUtils_1 = require("./GitignoreUtils");
|
|
45
|
+
const merge_1 = require("../mcp/merge");
|
|
46
|
+
const mcp_1 = require("../paths/mcp");
|
|
47
|
+
const propagateOpenHandsMcp_1 = require("../mcp/propagateOpenHandsMcp");
|
|
48
|
+
const propagateOpenCodeMcp_1 = require("../mcp/propagateOpenCodeMcp");
|
|
49
|
+
const agent_utils_1 = require("../agents/agent-utils");
|
|
50
|
+
const capabilities_1 = require("../mcp/capabilities");
|
|
51
|
+
const constants_1 = require("../constants");
|
|
52
|
+
/**
|
|
53
|
+
* Loads all necessary configurations for ruler operation.
|
|
54
|
+
* @param projectRoot Root directory of the project
|
|
55
|
+
* @param configPath Optional custom config path
|
|
56
|
+
* @param localOnly Whether to search only locally for .ruler directory
|
|
57
|
+
* @returns Promise resolving to the loaded configuration
|
|
58
|
+
*/
|
|
59
|
+
async function loadRulerConfiguration(projectRoot, configPath, localOnly) {
|
|
60
|
+
// Find the .ruler directory
|
|
61
|
+
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
|
|
62
|
+
if (!rulerDir) {
|
|
63
|
+
throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
|
|
64
|
+
}
|
|
65
|
+
// Load the ruler.toml configuration
|
|
66
|
+
const config = await (0, ConfigLoader_1.loadConfig)({
|
|
67
|
+
projectRoot,
|
|
68
|
+
configPath,
|
|
69
|
+
});
|
|
70
|
+
// Read and concatenate the markdown rule files
|
|
71
|
+
const files = await FileSystemUtils.readMarkdownFiles(rulerDir);
|
|
72
|
+
const concatenatedRules = (0, RuleProcessor_1.concatenateRules)(files, path.dirname(rulerDir));
|
|
73
|
+
// Load unified config to get merged MCP configuration
|
|
74
|
+
const { loadUnifiedConfig } = await Promise.resolve().then(() => __importStar(require('./UnifiedConfigLoader')));
|
|
75
|
+
const unifiedConfig = await loadUnifiedConfig({ projectRoot, configPath });
|
|
76
|
+
// Synthesize rulerMcpJson from unified MCP bundle for backward compatibility
|
|
77
|
+
let rulerMcpJson = null;
|
|
78
|
+
if (unifiedConfig.mcp && Object.keys(unifiedConfig.mcp.servers).length > 0) {
|
|
79
|
+
rulerMcpJson = {
|
|
80
|
+
mcpServers: unifiedConfig.mcp.servers,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
config,
|
|
85
|
+
concatenatedRules,
|
|
86
|
+
rulerMcpJson,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Selects the agents to process based on configuration.
|
|
91
|
+
* @param allAgents Array of all available agents
|
|
92
|
+
* @param config Loaded configuration
|
|
93
|
+
* @returns Array of agents to be processed
|
|
94
|
+
*/
|
|
95
|
+
function selectAgentsToRun(allAgents, config) {
|
|
96
|
+
// CLI --agents > config.default_agents > per-agent.enabled flags > default all
|
|
97
|
+
let selected = allAgents;
|
|
98
|
+
if (config.cliAgents && config.cliAgents.length > 0) {
|
|
99
|
+
const filters = config.cliAgents.map((n) => n.toLowerCase());
|
|
100
|
+
// Check if any of the specified agents don't exist
|
|
101
|
+
const validAgentIdentifiers = new Set(allAgents.map((agent) => agent.getIdentifier()));
|
|
102
|
+
const validAgentNames = new Set(allAgents.map((agent) => agent.getName().toLowerCase()));
|
|
103
|
+
const invalidAgents = filters.filter((filter) => !validAgentIdentifiers.has(filter) &&
|
|
104
|
+
![...validAgentNames].some((name) => name.includes(filter)));
|
|
105
|
+
if (invalidAgents.length > 0) {
|
|
106
|
+
throw (0, constants_1.createRulerError)(`Invalid agent specified: ${invalidAgents.join(', ')}`, `Valid agents are: ${[...validAgentIdentifiers].join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
selected = allAgents.filter((agent) => filters.some((f) => agent.getIdentifier() === f ||
|
|
109
|
+
agent.getName().toLowerCase().includes(f)));
|
|
110
|
+
}
|
|
111
|
+
else if (config.defaultAgents && config.defaultAgents.length > 0) {
|
|
112
|
+
const defaults = config.defaultAgents.map((n) => n.toLowerCase());
|
|
113
|
+
// Check if any of the default agents don't exist
|
|
114
|
+
const validAgentIdentifiers = new Set(allAgents.map((agent) => agent.getIdentifier()));
|
|
115
|
+
const validAgentNames = new Set(allAgents.map((agent) => agent.getName().toLowerCase()));
|
|
116
|
+
const invalidAgents = defaults.filter((filter) => !validAgentIdentifiers.has(filter) &&
|
|
117
|
+
![...validAgentNames].some((name) => name.includes(filter)));
|
|
118
|
+
if (invalidAgents.length > 0) {
|
|
119
|
+
throw (0, constants_1.createRulerError)(`Invalid agent specified in default_agents: ${invalidAgents.join(', ')}`, `Valid agents are: ${[...validAgentIdentifiers].join(', ')}`);
|
|
120
|
+
}
|
|
121
|
+
selected = allAgents.filter((agent) => {
|
|
122
|
+
const identifier = agent.getIdentifier();
|
|
123
|
+
const override = config.agentConfigs[identifier]?.enabled;
|
|
124
|
+
if (override !== undefined) {
|
|
125
|
+
return override;
|
|
126
|
+
}
|
|
127
|
+
return defaults.some((d) => identifier === d || agent.getName().toLowerCase().includes(d));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
selected = allAgents.filter((agent) => config.agentConfigs[agent.getIdentifier()]?.enabled !== false);
|
|
132
|
+
}
|
|
133
|
+
return selected;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Applies configurations to the selected agents.
|
|
137
|
+
* @param agents Array of agents to process
|
|
138
|
+
* @param concatenatedRules Concatenated rule content
|
|
139
|
+
* @param rulerMcpJson MCP configuration JSON
|
|
140
|
+
* @param config Loaded configuration
|
|
141
|
+
* @param projectRoot Root directory of the project
|
|
142
|
+
* @param verbose Whether to enable verbose logging
|
|
143
|
+
* @param dryRun Whether to perform a dry run
|
|
144
|
+
* @returns Promise resolving to array of generated file paths
|
|
145
|
+
*/
|
|
146
|
+
async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJson, config, projectRoot, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy) {
|
|
147
|
+
const generatedPaths = [];
|
|
148
|
+
let agentsMdWritten = false;
|
|
149
|
+
for (const agent of agents) {
|
|
150
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
151
|
+
console.log(`${prefix} Applying rules for ${agent.getName()}...`);
|
|
152
|
+
(0, constants_1.logVerbose)(`Processing agent: ${agent.getName()}`, verbose);
|
|
153
|
+
const agentConfig = config.agentConfigs[agent.getIdentifier()];
|
|
154
|
+
// Collect output paths for .gitignore
|
|
155
|
+
const outputPaths = (0, agent_utils_1.getAgentOutputPaths)(agent, projectRoot, agentConfig);
|
|
156
|
+
(0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
|
|
157
|
+
generatedPaths.push(...outputPaths);
|
|
158
|
+
// Also add the backup file paths to the gitignore list
|
|
159
|
+
const backupPaths = outputPaths.map((p) => `${p}.bak`);
|
|
160
|
+
generatedPaths.push(...backupPaths);
|
|
161
|
+
if (dryRun) {
|
|
162
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would write rules to: ${outputPaths.join(', ')}`, verbose);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
let skipApplyForThisAgent = false;
|
|
166
|
+
if (agent.getIdentifier() === 'jules' ||
|
|
167
|
+
agent.getIdentifier() === 'agentsmd') {
|
|
168
|
+
if (agentsMdWritten) {
|
|
169
|
+
// Skip rewriting AGENTS.md, but still allow MCP handling below
|
|
170
|
+
skipApplyForThisAgent = true;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
agentsMdWritten = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
let finalAgentConfig = agentConfig;
|
|
177
|
+
if (agent.getIdentifier() === 'augmentcode' && rulerMcpJson) {
|
|
178
|
+
const resolvedStrategy = cliMcpStrategy ??
|
|
179
|
+
agentConfig?.mcp?.strategy ??
|
|
180
|
+
config.mcp?.strategy ??
|
|
181
|
+
'merge';
|
|
182
|
+
finalAgentConfig = {
|
|
183
|
+
...agentConfig,
|
|
184
|
+
mcp: {
|
|
185
|
+
...agentConfig?.mcp,
|
|
186
|
+
strategy: resolvedStrategy,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (!skipApplyForThisAgent) {
|
|
191
|
+
await agent.applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, finalAgentConfig);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Handle MCP configuration
|
|
195
|
+
await handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled, cliMcpStrategy);
|
|
196
|
+
}
|
|
197
|
+
return generatedPaths;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Handles MCP configuration for a specific agent.
|
|
201
|
+
*/
|
|
202
|
+
async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy) {
|
|
203
|
+
// Check if agent supports MCP at all
|
|
204
|
+
if (!(0, capabilities_1.agentSupportsMcp)(agent)) {
|
|
205
|
+
(0, constants_1.logVerbose)(`Agent ${agent.getName()} does not support MCP - skipping MCP configuration`, verbose);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
|
|
209
|
+
const mcpEnabledForAgent = cliMcpEnabled && (agentConfig?.mcp?.enabled ?? config.mcp?.enabled ?? true);
|
|
210
|
+
if (dest && mcpEnabledForAgent && rulerMcpJson) {
|
|
211
|
+
// Filter MCP configuration based on agent capabilities
|
|
212
|
+
const filteredMcpJson = (0, capabilities_1.filterMcpConfigForAgent)(rulerMcpJson, agent);
|
|
213
|
+
if (!filteredMcpJson) {
|
|
214
|
+
(0, constants_1.logVerbose)(`No compatible MCP servers found for ${agent.getName()} - skipping MCP configuration`, verbose);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// Include MCP config file in .gitignore only if it's within the project directory
|
|
218
|
+
if (dest.startsWith(projectRoot)) {
|
|
219
|
+
const relativeDest = path.relative(projectRoot, dest);
|
|
220
|
+
generatedPaths.push(relativeDest);
|
|
221
|
+
// Also add the backup for the MCP file
|
|
222
|
+
generatedPaths.push(`${relativeDest}.bak`);
|
|
223
|
+
}
|
|
224
|
+
// Prevent writing MCP configs outside the project root (e.g., legacy home-directory targets)
|
|
225
|
+
if (!dest.startsWith(projectRoot)) {
|
|
226
|
+
(0, constants_1.logVerbose)(`Skipping MCP config for ${agent.getName()} because target path is outside project: ${dest}`, verbose);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (agent.getIdentifier() === 'openhands') {
|
|
230
|
+
// *** Special handling for Open Hands ***
|
|
231
|
+
if (dryRun) {
|
|
232
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating TOML file: ${dest}`, verbose);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
await (0, propagateOpenHandsMcp_1.propagateMcpToOpenHands)(filteredMcpJson, dest);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else if (agent.getIdentifier() === 'opencode') {
|
|
239
|
+
// *** Special handling for OpenCode ***
|
|
240
|
+
if (dryRun) {
|
|
241
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating OpenCode config file: ${dest}`, verbose);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
await (0, propagateOpenCodeMcp_1.propagateMcpToOpenCode)(filteredMcpJson, dest);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Standard MCP handling using capabilities
|
|
249
|
+
const strategy = cliMcpStrategy ??
|
|
250
|
+
agentConfig?.mcp?.strategy ??
|
|
251
|
+
config.mcp?.strategy ??
|
|
252
|
+
'merge';
|
|
253
|
+
// Determine the correct server key for the agent
|
|
254
|
+
const serverKey = agent.getMcpServerKey?.() || 'mcpServers';
|
|
255
|
+
(0, constants_1.logVerbose)(`Applying filtered MCP config for ${agent.getName()} with strategy: ${strategy} and key: ${serverKey}`, verbose);
|
|
256
|
+
if (dryRun) {
|
|
257
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config to: ${dest}`, verbose);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
const existing = await (0, mcp_1.readNativeMcp)(dest);
|
|
261
|
+
const merged = (0, merge_1.mergeMcp)(existing, filteredMcpJson, strategy, serverKey);
|
|
262
|
+
await (0, mcp_1.writeNativeMcp)(dest, merged);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Updates the .gitignore file with generated paths.
|
|
269
|
+
* @param projectRoot Root directory of the project
|
|
270
|
+
* @param generatedPaths Array of generated file paths
|
|
271
|
+
* @param config Loaded configuration
|
|
272
|
+
* @param cliGitignoreEnabled CLI gitignore setting
|
|
273
|
+
* @param dryRun Whether to perform a dry run
|
|
274
|
+
*/
|
|
275
|
+
async function updateGitignore(projectRoot, generatedPaths, config, cliGitignoreEnabled, dryRun) {
|
|
276
|
+
// Configuration precedence: CLI > TOML > Default (enabled)
|
|
277
|
+
let gitignoreEnabled;
|
|
278
|
+
if (cliGitignoreEnabled !== undefined) {
|
|
279
|
+
gitignoreEnabled = cliGitignoreEnabled;
|
|
280
|
+
}
|
|
281
|
+
else if (config.gitignore?.enabled !== undefined) {
|
|
282
|
+
gitignoreEnabled = config.gitignore.enabled;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
gitignoreEnabled = true; // Default enabled
|
|
286
|
+
}
|
|
287
|
+
if (gitignoreEnabled && generatedPaths.length > 0) {
|
|
288
|
+
const uniquePaths = [...new Set(generatedPaths)];
|
|
289
|
+
// Add wildcard pattern for backup files
|
|
290
|
+
uniquePaths.push('*.bak');
|
|
291
|
+
if (uniquePaths.length > 0) {
|
|
292
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
293
|
+
if (dryRun) {
|
|
294
|
+
console.log(`${prefix} Would update .gitignore with ${uniquePaths.length} unique path(s): ${uniquePaths.join(', ')}`);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
await (0, GitignoreUtils_1.updateGitignore)(projectRoot, uniquePaths);
|
|
298
|
+
console.log(`${prefix} Updated .gitignore with ${uniquePaths.length} unique path(s) in the Ruler block.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapRawAgentConfigs = mapRawAgentConfigs;
|
|
4
|
+
/**
|
|
5
|
+
* Maps raw agent configuration keys to their corresponding agent identifiers.
|
|
6
|
+
*
|
|
7
|
+
* This function normalizes configuration keys by matching them against agent identifiers
|
|
8
|
+
* and display names. It performs both exact matching (case-insensitive) with agent
|
|
9
|
+
* identifiers and substring matching (case-insensitive) with agent display names
|
|
10
|
+
* for backwards compatibility.
|
|
11
|
+
*
|
|
12
|
+
* @param raw Raw agent configurations with user-provided keys
|
|
13
|
+
* @param agents Array of all available agents
|
|
14
|
+
* @returns Record with agent identifiers as keys and their configurations as values
|
|
15
|
+
*/
|
|
16
|
+
function mapRawAgentConfigs(raw, agents) {
|
|
17
|
+
const mappedConfigs = {};
|
|
18
|
+
for (const [key, cfg] of Object.entries(raw)) {
|
|
19
|
+
const lowerKey = key.toLowerCase();
|
|
20
|
+
for (const agent of agents) {
|
|
21
|
+
const identifier = agent.getIdentifier();
|
|
22
|
+
// Exact match with identifier or substring match with display name for backwards compatibility
|
|
23
|
+
if (identifier === lowerKey ||
|
|
24
|
+
agent.getName().toLowerCase().includes(lowerKey)) {
|
|
25
|
+
mappedConfigs[identifier] = cfg;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return mappedConfigs;
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sha256 = sha256;
|
|
4
|
+
exports.stableJson = stableJson;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
function sha256(data) {
|
|
7
|
+
return (0, crypto_1.createHash)('sha256').update(data, 'utf8').digest('hex');
|
|
8
|
+
}
|
|
9
|
+
// Stable JSON stringify: sorts object keys recursively.
|
|
10
|
+
function stableJson(value) {
|
|
11
|
+
return JSON.stringify(sortValue(value));
|
|
12
|
+
}
|
|
13
|
+
function sortValue(value) {
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return value.map(sortValue);
|
|
16
|
+
}
|
|
17
|
+
if (value && typeof value === 'object') {
|
|
18
|
+
const entries = Object.entries(value)
|
|
19
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
20
|
+
.map(([k, v]) => [k, sortValue(v)]);
|
|
21
|
+
return Object.fromEntries(entries);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|