rulesync 43.6.1 → 43.6.3
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/cli/index.cjs +7 -10
- package/dist/cli/index.js +7 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-BzVkMPd5.cjs → import-DCvPujUW.cjs} +656 -344
- package/dist/{import-Safgeb1C.js → import-w0JlEoNl.js} +652 -340
- package/dist/{import-Safgeb1C.js.map → import-w0JlEoNl.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +8 -8
|
@@ -2291,6 +2291,190 @@ async function removeTempDirectory(tempDir) {
|
|
|
2291
2291
|
});
|
|
2292
2292
|
} catch {}
|
|
2293
2293
|
}
|
|
2294
|
+
// src/utils/prototype-pollution.ts
|
|
2295
|
+
/**
|
|
2296
|
+
* Keys that, if walked into when constructing or merging objects from
|
|
2297
|
+
* untrusted input, can mutate `Object.prototype` (or otherwise the prototype
|
|
2298
|
+
* chain) and propagate state to every other object in the runtime. Any code
|
|
2299
|
+
* that copies arbitrary user-supplied keys into a fresh object — frontmatter
|
|
2300
|
+
* parsing, MCP config conversion, settings round-trip — should skip these.
|
|
2301
|
+
*/
|
|
2302
|
+
const PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
2303
|
+
"__proto__",
|
|
2304
|
+
"constructor",
|
|
2305
|
+
"prototype"
|
|
2306
|
+
]);
|
|
2307
|
+
function isPrototypePollutionKey(key) {
|
|
2308
|
+
return PROTOTYPE_POLLUTION_KEYS.has(key);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Returns a shallow copy of a record's own entries with every
|
|
2312
|
+
* prototype-pollution key (`__proto__`, `constructor`, `prototype`) dropped.
|
|
2313
|
+
*
|
|
2314
|
+
* Use when copying a nested, user-supplied string map — an MCP server's `env`
|
|
2315
|
+
* or `headers` table — into freshly generated config. Carrying such a map by
|
|
2316
|
+
* reference, or re-assigning its keys via bracket notation, would let a literal
|
|
2317
|
+
* `__proto__` key ride along (and re-assigning it would mutate the target's
|
|
2318
|
+
* prototype). Walking the entries through this helper severs that path while
|
|
2319
|
+
* preserving every legitimate key.
|
|
2320
|
+
*/
|
|
2321
|
+
function omitPrototypePollutionKeys(record) {
|
|
2322
|
+
const sanitized = {};
|
|
2323
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2324
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2325
|
+
sanitized[key] = value;
|
|
2326
|
+
}
|
|
2327
|
+
return sanitized;
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Like {@link omitPrototypePollutionKeys}, but recursive: every plain object
|
|
2331
|
+
* reachable through the value (directly, or through an array) is copied with
|
|
2332
|
+
* its prototype-pollution keys dropped. Scalars and arrays of scalars come back
|
|
2333
|
+
* as they are.
|
|
2334
|
+
*
|
|
2335
|
+
* Use when passing an unknown, user-supplied value through unchanged — an MCP
|
|
2336
|
+
* server field a tool documents that rulesync does not model — so a
|
|
2337
|
+
* `__proto__` key nested anywhere inside it cannot ride into the generated
|
|
2338
|
+
* config, where the consuming tool may merge the object without the same care.
|
|
2339
|
+
*/
|
|
2340
|
+
function omitPrototypePollutionKeysDeep(value) {
|
|
2341
|
+
if (Array.isArray(value)) return value.map((entry) => omitPrototypePollutionKeysDeep(entry));
|
|
2342
|
+
if (value === null || typeof value !== "object") return value;
|
|
2343
|
+
const sanitized = {};
|
|
2344
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2345
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2346
|
+
sanitized[key] = omitPrototypePollutionKeysDeep(entry);
|
|
2347
|
+
}
|
|
2348
|
+
return sanitized;
|
|
2349
|
+
}
|
|
2350
|
+
// src/utils/jsonc.ts
|
|
2351
|
+
/**
|
|
2352
|
+
* Rebuild the parsed value from its own enumerable entries, dropping
|
|
2353
|
+
* prototype-pollution keys (`__proto__`, `constructor`, `prototype`).
|
|
2354
|
+
* `jsonc-parser` assigns keys with plain `obj[key] = value` semantics, so a
|
|
2355
|
+
* literal `__proto__` key would replace the containing object's prototype
|
|
2356
|
+
* instead of becoming an own property; rebuilding gives every object a clean
|
|
2357
|
+
* `Object.prototype` again and severs that path.
|
|
2358
|
+
*/
|
|
2359
|
+
function deepSanitize(value) {
|
|
2360
|
+
if (Array.isArray(value)) return value.map((item) => deepSanitize(item));
|
|
2361
|
+
if (value !== null && typeof value === "object") {
|
|
2362
|
+
const sanitized = {};
|
|
2363
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2364
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2365
|
+
sanitized[key] = deepSanitize(entry);
|
|
2366
|
+
}
|
|
2367
|
+
return sanitized;
|
|
2368
|
+
}
|
|
2369
|
+
return value;
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Collect the paths of every prototype-pollution key in the document, walking
|
|
2373
|
+
* the syntax tree rather than the parsed value.
|
|
2374
|
+
*
|
|
2375
|
+
* The parsed value cannot answer this question for `__proto__`:
|
|
2376
|
+
* `obj["__proto__"] = "deny"` is discarded by the engine outright, and
|
|
2377
|
+
* `obj["__proto__"] = {...}` replaces the prototype instead of adding an own
|
|
2378
|
+
* property — either way the key is already gone by the time
|
|
2379
|
+
* {@link deepSanitize} could look for it, and only the source text still knows
|
|
2380
|
+
* the user wrote it. `constructor` and `prototype` do become own properties and
|
|
2381
|
+
* could be found in the parsed value, but they are collected here too so all
|
|
2382
|
+
* three keys are answered the same way.
|
|
2383
|
+
*
|
|
2384
|
+
* A pollution key's subtree is not descended into, matching
|
|
2385
|
+
* {@link deepSanitize}, which drops the whole value along with the key.
|
|
2386
|
+
*/
|
|
2387
|
+
function collectPollutionKeyPaths({ node, path, found }) {
|
|
2388
|
+
if (node.type === "array") {
|
|
2389
|
+
for (const [index, child] of (node.children ?? []).entries()) collectPollutionKeyPaths({
|
|
2390
|
+
node: child,
|
|
2391
|
+
path: `${path}[${index}]`,
|
|
2392
|
+
found
|
|
2393
|
+
});
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
if (node.type !== "object") return;
|
|
2397
|
+
for (const property of node.children ?? []) {
|
|
2398
|
+
const [keyNode, valueNode] = property.children ?? [];
|
|
2399
|
+
if (typeof keyNode?.value !== "string") continue;
|
|
2400
|
+
const keyPath = path === "" ? keyNode.value : `${path}.${keyNode.value}`;
|
|
2401
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(keyNode.value)) {
|
|
2402
|
+
found.push(keyPath);
|
|
2403
|
+
continue;
|
|
2404
|
+
}
|
|
2405
|
+
if (valueNode !== void 0) collectPollutionKeyPaths({
|
|
2406
|
+
node: valueNode,
|
|
2407
|
+
path: keyPath,
|
|
2408
|
+
found
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Parse a JSONC (JSON with Comments) document strictly.
|
|
2414
|
+
*
|
|
2415
|
+
* Unlike `jsonc-parser`'s bare `parse` (which silently tolerates syntax
|
|
2416
|
+
* errors and returns a best-effort value), this throws on any parse error so
|
|
2417
|
+
* a malformed source file fails loudly instead of generating half-empty tool
|
|
2418
|
+
* configs. Plain JSON is valid JSONC, so this is a drop-in replacement for
|
|
2419
|
+
* `JSON.parse` on files that may contain comments or trailing commas.
|
|
2420
|
+
*/
|
|
2421
|
+
function parseJsonc$6(content) {
|
|
2422
|
+
return deepSanitize(parseStrict(content));
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* The strict parse shared by both entry points. Kept separate from the syntax
|
|
2426
|
+
* tree walk so the many callers that never look at `droppedKeys` do not pay
|
|
2427
|
+
* for building a second representation of the document.
|
|
2428
|
+
*/
|
|
2429
|
+
function parseStrict(content) {
|
|
2430
|
+
const errors = [];
|
|
2431
|
+
const result = (0, jsonc_parser.parse)(content, errors, {
|
|
2432
|
+
allowTrailingComma: true,
|
|
2433
|
+
disallowComments: false
|
|
2434
|
+
});
|
|
2435
|
+
if (errors.length > 0) {
|
|
2436
|
+
const details = errors.map((e) => `${(0, jsonc_parser.printParseErrorCode)(e.error)} at offset ${e.offset}`).join(", ");
|
|
2437
|
+
throw new SyntaxError(`Failed to parse JSONC content: ${details}`);
|
|
2438
|
+
}
|
|
2439
|
+
return result;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* The error a source document should fail with when
|
|
2443
|
+
* {@link parseJsoncReportingDroppedKeys} reports keys the parser removed.
|
|
2444
|
+
*
|
|
2445
|
+
* Shared by every source that reports them, so the three files a user can
|
|
2446
|
+
* author explain the same removal the same way rather than each inventing its
|
|
2447
|
+
* own wording.
|
|
2448
|
+
*/
|
|
2449
|
+
function droppedPollutionKeysError({ sourcePath, droppedKeys }) {
|
|
2450
|
+
return /* @__PURE__ */ new Error(`${JSON.stringify(sourcePath)} uses ${droppedKeys.map((key) => JSON.stringify(key)).join(", ")} as ${droppedKeys.length === 1 ? "a key" : "keys"}. Rulesync removes __proto__, constructor and prototype from every source document it parses, because assigning them would reach the prototype chain instead of the object. They are therefore never written to any tool's config — rename them rather than leaving entries that silently do nothing.`);
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* The same parse as {@link parseJsonc}, additionally reporting which
|
|
2454
|
+
* prototype-pollution keys were removed, as dotted paths
|
|
2455
|
+
* (`permission.bash.__proto__`).
|
|
2456
|
+
*
|
|
2457
|
+
* The parse itself is identical — the paths only let a caller tell the user
|
|
2458
|
+
* about entries that vanished, which they would otherwise have to notice by
|
|
2459
|
+
* comparing their source file against the generated output.
|
|
2460
|
+
*/
|
|
2461
|
+
function parseJsoncReportingDroppedKeys({ content }) {
|
|
2462
|
+
const result = parseStrict(content);
|
|
2463
|
+
const found = [];
|
|
2464
|
+
const tree = (0, jsonc_parser.parseTree)(content, [], {
|
|
2465
|
+
allowTrailingComma: true,
|
|
2466
|
+
disallowComments: false
|
|
2467
|
+
});
|
|
2468
|
+
if (tree !== void 0) collectPollutionKeyPaths({
|
|
2469
|
+
node: tree,
|
|
2470
|
+
path: "",
|
|
2471
|
+
found
|
|
2472
|
+
});
|
|
2473
|
+
return {
|
|
2474
|
+
value: deepSanitize(result),
|
|
2475
|
+
droppedKeys: [...new Set(found)]
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2294
2478
|
// src/types/json-output.ts
|
|
2295
2479
|
/**
|
|
2296
2480
|
* Error codes for CLI errors
|
|
@@ -3580,10 +3764,37 @@ const getDefaults = () => ({
|
|
|
3580
3764
|
inputRoots: void 0,
|
|
3581
3765
|
sources: []
|
|
3582
3766
|
});
|
|
3767
|
+
/**
|
|
3768
|
+
* Parse the content of a config file (`rulesync.jsonc` / `rulesync.local.jsonc`)
|
|
3769
|
+
* through the same strict, sanitizing JSONC parser as every other rulesync
|
|
3770
|
+
* source.
|
|
3771
|
+
*
|
|
3772
|
+
* `jsonc-parser`'s bare `parse` recovers from syntax errors without reporting
|
|
3773
|
+
* them, which let a typo (a missing comma, unquoted keys, trailing garbage)
|
|
3774
|
+
* silently drop keys and run rulesync with a different config than the user
|
|
3775
|
+
* wrote. Prototype-pollution keys are rejected rather than silently removed,
|
|
3776
|
+
* matching the hooks, MCP and permissions sources.
|
|
3777
|
+
*/
|
|
3778
|
+
function parseConfigFileContent({ content, filePath }) {
|
|
3779
|
+
let result;
|
|
3780
|
+
try {
|
|
3781
|
+
result = parseJsoncReportingDroppedKeys({ content });
|
|
3782
|
+
} catch (error) {
|
|
3783
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3784
|
+
throw new Error(`Failed to parse ${JSON.stringify(filePath)}: ${detail}`, { cause: error });
|
|
3785
|
+
}
|
|
3786
|
+
if (result.droppedKeys.length > 0) throw droppedPollutionKeysError({
|
|
3787
|
+
sourcePath: filePath,
|
|
3788
|
+
droppedKeys: result.droppedKeys
|
|
3789
|
+
});
|
|
3790
|
+
return result.value;
|
|
3791
|
+
}
|
|
3583
3792
|
const loadConfigFromFile = async (filePath) => {
|
|
3584
3793
|
if (!await fileExists(filePath)) return {};
|
|
3585
|
-
const
|
|
3586
|
-
|
|
3794
|
+
const jsonData = parseConfigFileContent({
|
|
3795
|
+
content: await readFileContent(filePath),
|
|
3796
|
+
filePath
|
|
3797
|
+
});
|
|
3587
3798
|
const { $schema: _schema, ...configParams } = ConfigFileSchema.parse(jsonData);
|
|
3588
3799
|
assertTargetsFeaturesExclusive({
|
|
3589
3800
|
targets: configParams.targets,
|
|
@@ -4196,62 +4407,6 @@ function createBoundedWalk({ subject, limits, root }) {
|
|
|
4196
4407
|
leave
|
|
4197
4408
|
};
|
|
4198
4409
|
}
|
|
4199
|
-
// src/utils/prototype-pollution.ts
|
|
4200
|
-
/**
|
|
4201
|
-
* Keys that, if walked into when constructing or merging objects from
|
|
4202
|
-
* untrusted input, can mutate `Object.prototype` (or otherwise the prototype
|
|
4203
|
-
* chain) and propagate state to every other object in the runtime. Any code
|
|
4204
|
-
* that copies arbitrary user-supplied keys into a fresh object — frontmatter
|
|
4205
|
-
* parsing, MCP config conversion, settings round-trip — should skip these.
|
|
4206
|
-
*/
|
|
4207
|
-
const PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
4208
|
-
"__proto__",
|
|
4209
|
-
"constructor",
|
|
4210
|
-
"prototype"
|
|
4211
|
-
]);
|
|
4212
|
-
function isPrototypePollutionKey(key) {
|
|
4213
|
-
return PROTOTYPE_POLLUTION_KEYS.has(key);
|
|
4214
|
-
}
|
|
4215
|
-
/**
|
|
4216
|
-
* Returns a shallow copy of a record's own entries with every
|
|
4217
|
-
* prototype-pollution key (`__proto__`, `constructor`, `prototype`) dropped.
|
|
4218
|
-
*
|
|
4219
|
-
* Use when copying a nested, user-supplied string map — an MCP server's `env`
|
|
4220
|
-
* or `headers` table — into freshly generated config. Carrying such a map by
|
|
4221
|
-
* reference, or re-assigning its keys via bracket notation, would let a literal
|
|
4222
|
-
* `__proto__` key ride along (and re-assigning it would mutate the target's
|
|
4223
|
-
* prototype). Walking the entries through this helper severs that path while
|
|
4224
|
-
* preserving every legitimate key.
|
|
4225
|
-
*/
|
|
4226
|
-
function omitPrototypePollutionKeys(record) {
|
|
4227
|
-
const sanitized = {};
|
|
4228
|
-
for (const [key, value] of Object.entries(record)) {
|
|
4229
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4230
|
-
sanitized[key] = value;
|
|
4231
|
-
}
|
|
4232
|
-
return sanitized;
|
|
4233
|
-
}
|
|
4234
|
-
/**
|
|
4235
|
-
* Like {@link omitPrototypePollutionKeys}, but recursive: every plain object
|
|
4236
|
-
* reachable through the value (directly, or through an array) is copied with
|
|
4237
|
-
* its prototype-pollution keys dropped. Scalars and arrays of scalars come back
|
|
4238
|
-
* as they are.
|
|
4239
|
-
*
|
|
4240
|
-
* Use when passing an unknown, user-supplied value through unchanged — an MCP
|
|
4241
|
-
* server field a tool documents that rulesync does not model — so a
|
|
4242
|
-
* `__proto__` key nested anywhere inside it cannot ride into the generated
|
|
4243
|
-
* config, where the consuming tool may merge the object without the same care.
|
|
4244
|
-
*/
|
|
4245
|
-
function omitPrototypePollutionKeysDeep(value) {
|
|
4246
|
-
if (Array.isArray(value)) return value.map((entry) => omitPrototypePollutionKeysDeep(entry));
|
|
4247
|
-
if (value === null || typeof value !== "object") return value;
|
|
4248
|
-
const sanitized = {};
|
|
4249
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
4250
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4251
|
-
sanitized[key] = omitPrototypePollutionKeysDeep(entry);
|
|
4252
|
-
}
|
|
4253
|
-
return sanitized;
|
|
4254
|
-
}
|
|
4255
4410
|
// src/utils/type-guards.ts
|
|
4256
4411
|
/**
|
|
4257
4412
|
* Type guard to check if a value is a plain object (Record<string, unknown>).
|
|
@@ -4879,6 +5034,24 @@ function lookupOwn({ record, key }) {
|
|
|
4879
5034
|
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
4880
5035
|
}
|
|
4881
5036
|
/**
|
|
5037
|
+
* Return the value a map holds under its own `key`, first storing `create()`
|
|
5038
|
+
* there when it has none. A bracket read would find an inherited member for a
|
|
5039
|
+
* key such as `toString`, so an importer bucketing rules by a user-supplied
|
|
5040
|
+
* name would write into an `Object.prototype` function instead of a fresh
|
|
5041
|
+
* bucket.
|
|
5042
|
+
*/
|
|
5043
|
+
function getOrCreateOwn({ record, key, create }) {
|
|
5044
|
+
if (Object.hasOwn(record, key)) return record[key];
|
|
5045
|
+
const created = create();
|
|
5046
|
+
Object.defineProperty(record, key, {
|
|
5047
|
+
value: created,
|
|
5048
|
+
writable: true,
|
|
5049
|
+
enumerable: true,
|
|
5050
|
+
configurable: true
|
|
5051
|
+
});
|
|
5052
|
+
return created;
|
|
5053
|
+
}
|
|
5054
|
+
/**
|
|
4882
5055
|
* Swap the keys and values of a name map, e.g. a canonical-to-tool map into
|
|
4883
5056
|
* the tool-to-canonical map an importer reads. When two keys share a value,
|
|
4884
5057
|
* the later key wins.
|
|
@@ -6413,14 +6586,16 @@ const CANONICAL_TO_GOOSE_EVENT_NAMES = {
|
|
|
6413
6586
|
* Hook events supported by Goose.
|
|
6414
6587
|
*
|
|
6415
6588
|
* Goose adopts the Open Plugins hooks spec: each plugin's `hooks/hooks.json`
|
|
6416
|
-
* maps PascalCase event names to matcher/handler arrays.
|
|
6417
|
-
*
|
|
6418
|
-
*
|
|
6419
|
-
*
|
|
6420
|
-
*
|
|
6421
|
-
*
|
|
6589
|
+
* maps PascalCase event names to matcher/handler arrays.
|
|
6590
|
+
*
|
|
6591
|
+
* Goose's `HookEvent` enum defines these 11 events plus `PreToolUseResult`
|
|
6592
|
+
* (added in v1.48.0), which has no canonical equivalent: it is authored under
|
|
6593
|
+
* the `goose.hooks` override block by its native name, emitted verbatim, and
|
|
6594
|
+
* imported back into that block. Notably the enum has NO
|
|
6595
|
+
* `SubagentStart`/`SubagentStop` arms — emitting them would write keys Goose
|
|
6596
|
+
* silently ignores, so `subagentStart`/`subagentStop` are intentionally
|
|
6422
6597
|
* excluded here and from `CANONICAL_TO_GOOSE_EVENT_NAMES`.
|
|
6423
|
-
* @see https://github.com/aaif-goose/goose/blob/v1.
|
|
6598
|
+
* @see https://github.com/aaif-goose/goose/blob/v1.48.0/crates/goose/src/hooks/mod.rs
|
|
6424
6599
|
* @see https://goose-docs.ai/docs/guides/context-engineering/hooks/
|
|
6425
6600
|
*/
|
|
6426
6601
|
const GOOSE_HOOK_EVENTS = eventsOf(CANONICAL_TO_GOOSE_EVENT_NAMES);
|
|
@@ -6898,134 +7073,6 @@ const GROKCLI_HOOK_EVENTS = eventsOf(CANONICAL_TO_GROKCLI_EVENT_NAMES);
|
|
|
6898
7073
|
* Map Grok CLI PascalCase event names to canonical camelCase.
|
|
6899
7074
|
*/
|
|
6900
7075
|
const GROKCLI_TO_CANONICAL_EVENT_NAMES = invertRecord(CANONICAL_TO_GROKCLI_EVENT_NAMES);
|
|
6901
|
-
// src/utils/jsonc.ts
|
|
6902
|
-
/**
|
|
6903
|
-
* Rebuild the parsed value from its own enumerable entries, dropping
|
|
6904
|
-
* prototype-pollution keys (`__proto__`, `constructor`, `prototype`).
|
|
6905
|
-
* `jsonc-parser` assigns keys with plain `obj[key] = value` semantics, so a
|
|
6906
|
-
* literal `__proto__` key would replace the containing object's prototype
|
|
6907
|
-
* instead of becoming an own property; rebuilding gives every object a clean
|
|
6908
|
-
* `Object.prototype` again and severs that path.
|
|
6909
|
-
*/
|
|
6910
|
-
function deepSanitize(value) {
|
|
6911
|
-
if (Array.isArray(value)) return value.map((item) => deepSanitize(item));
|
|
6912
|
-
if (value !== null && typeof value === "object") {
|
|
6913
|
-
const sanitized = {};
|
|
6914
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
6915
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
6916
|
-
sanitized[key] = deepSanitize(entry);
|
|
6917
|
-
}
|
|
6918
|
-
return sanitized;
|
|
6919
|
-
}
|
|
6920
|
-
return value;
|
|
6921
|
-
}
|
|
6922
|
-
/**
|
|
6923
|
-
* Collect the paths of every prototype-pollution key in the document, walking
|
|
6924
|
-
* the syntax tree rather than the parsed value.
|
|
6925
|
-
*
|
|
6926
|
-
* The parsed value cannot answer this question for `__proto__`:
|
|
6927
|
-
* `obj["__proto__"] = "deny"` is discarded by the engine outright, and
|
|
6928
|
-
* `obj["__proto__"] = {...}` replaces the prototype instead of adding an own
|
|
6929
|
-
* property — either way the key is already gone by the time
|
|
6930
|
-
* {@link deepSanitize} could look for it, and only the source text still knows
|
|
6931
|
-
* the user wrote it. `constructor` and `prototype` do become own properties and
|
|
6932
|
-
* could be found in the parsed value, but they are collected here too so all
|
|
6933
|
-
* three keys are answered the same way.
|
|
6934
|
-
*
|
|
6935
|
-
* A pollution key's subtree is not descended into, matching
|
|
6936
|
-
* {@link deepSanitize}, which drops the whole value along with the key.
|
|
6937
|
-
*/
|
|
6938
|
-
function collectPollutionKeyPaths({ node, path, found }) {
|
|
6939
|
-
if (node.type === "array") {
|
|
6940
|
-
for (const [index, child] of (node.children ?? []).entries()) collectPollutionKeyPaths({
|
|
6941
|
-
node: child,
|
|
6942
|
-
path: `${path}[${index}]`,
|
|
6943
|
-
found
|
|
6944
|
-
});
|
|
6945
|
-
return;
|
|
6946
|
-
}
|
|
6947
|
-
if (node.type !== "object") return;
|
|
6948
|
-
for (const property of node.children ?? []) {
|
|
6949
|
-
const [keyNode, valueNode] = property.children ?? [];
|
|
6950
|
-
if (typeof keyNode?.value !== "string") continue;
|
|
6951
|
-
const keyPath = path === "" ? keyNode.value : `${path}.${keyNode.value}`;
|
|
6952
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(keyNode.value)) {
|
|
6953
|
-
found.push(keyPath);
|
|
6954
|
-
continue;
|
|
6955
|
-
}
|
|
6956
|
-
if (valueNode !== void 0) collectPollutionKeyPaths({
|
|
6957
|
-
node: valueNode,
|
|
6958
|
-
path: keyPath,
|
|
6959
|
-
found
|
|
6960
|
-
});
|
|
6961
|
-
}
|
|
6962
|
-
}
|
|
6963
|
-
/**
|
|
6964
|
-
* Parse a JSONC (JSON with Comments) document strictly.
|
|
6965
|
-
*
|
|
6966
|
-
* Unlike `jsonc-parser`'s bare `parse` (which silently tolerates syntax
|
|
6967
|
-
* errors and returns a best-effort value), this throws on any parse error so
|
|
6968
|
-
* a malformed source file fails loudly instead of generating half-empty tool
|
|
6969
|
-
* configs. Plain JSON is valid JSONC, so this is a drop-in replacement for
|
|
6970
|
-
* `JSON.parse` on files that may contain comments or trailing commas.
|
|
6971
|
-
*/
|
|
6972
|
-
function parseJsonc$6(content) {
|
|
6973
|
-
return deepSanitize(parseStrict(content));
|
|
6974
|
-
}
|
|
6975
|
-
/**
|
|
6976
|
-
* The strict parse shared by both entry points. Kept separate from the syntax
|
|
6977
|
-
* tree walk so the many callers that never look at `droppedKeys` do not pay
|
|
6978
|
-
* for building a second representation of the document.
|
|
6979
|
-
*/
|
|
6980
|
-
function parseStrict(content) {
|
|
6981
|
-
const errors = [];
|
|
6982
|
-
const result = (0, jsonc_parser.parse)(content, errors, {
|
|
6983
|
-
allowTrailingComma: true,
|
|
6984
|
-
disallowComments: false
|
|
6985
|
-
});
|
|
6986
|
-
if (errors.length > 0) {
|
|
6987
|
-
const details = errors.map((e) => `${(0, jsonc_parser.printParseErrorCode)(e.error)} at offset ${e.offset}`).join(", ");
|
|
6988
|
-
throw new SyntaxError(`Failed to parse JSONC content: ${details}`);
|
|
6989
|
-
}
|
|
6990
|
-
return result;
|
|
6991
|
-
}
|
|
6992
|
-
/**
|
|
6993
|
-
* The error a source document should fail with when
|
|
6994
|
-
* {@link parseJsoncReportingDroppedKeys} reports keys the parser removed.
|
|
6995
|
-
*
|
|
6996
|
-
* Shared by every source that reports them, so the three files a user can
|
|
6997
|
-
* author explain the same removal the same way rather than each inventing its
|
|
6998
|
-
* own wording.
|
|
6999
|
-
*/
|
|
7000
|
-
function droppedPollutionKeysError({ sourcePath, droppedKeys }) {
|
|
7001
|
-
return /* @__PURE__ */ new Error(`${JSON.stringify(sourcePath)} uses ${droppedKeys.map((key) => JSON.stringify(key)).join(", ")} as ${droppedKeys.length === 1 ? "a key" : "keys"}. Rulesync removes __proto__, constructor and prototype from every source document it parses, because assigning them would reach the prototype chain instead of the object. They are therefore never written to any tool's config — rename them rather than leaving entries that silently do nothing.`);
|
|
7002
|
-
}
|
|
7003
|
-
/**
|
|
7004
|
-
* The same parse as {@link parseJsonc}, additionally reporting which
|
|
7005
|
-
* prototype-pollution keys were removed, as dotted paths
|
|
7006
|
-
* (`permission.bash.__proto__`).
|
|
7007
|
-
*
|
|
7008
|
-
* The parse itself is identical — the paths only let a caller tell the user
|
|
7009
|
-
* about entries that vanished, which they would otherwise have to notice by
|
|
7010
|
-
* comparing their source file against the generated output.
|
|
7011
|
-
*/
|
|
7012
|
-
function parseJsoncReportingDroppedKeys({ content }) {
|
|
7013
|
-
const result = parseStrict(content);
|
|
7014
|
-
const found = [];
|
|
7015
|
-
const tree = (0, jsonc_parser.parseTree)(content, [], {
|
|
7016
|
-
allowTrailingComma: true,
|
|
7017
|
-
disallowComments: false
|
|
7018
|
-
});
|
|
7019
|
-
if (tree !== void 0) collectPollutionKeyPaths({
|
|
7020
|
-
node: tree,
|
|
7021
|
-
path: "",
|
|
7022
|
-
found
|
|
7023
|
-
});
|
|
7024
|
-
return {
|
|
7025
|
-
value: deepSanitize(result),
|
|
7026
|
-
droppedKeys: [...new Set(found)]
|
|
7027
|
-
};
|
|
7028
|
-
}
|
|
7029
7076
|
// src/utils/rulesync-source-path.ts
|
|
7030
7077
|
/**
|
|
7031
7078
|
* No `.rulesync/` source file exists for a feature at any of its candidate
|
|
@@ -9698,11 +9745,9 @@ var RulesyncPermissions = class RulesyncPermissions extends RulesyncFile {
|
|
|
9698
9745
|
* `PERMISSION_OVERRIDE_KEY_ALIASES`) is replaced wholesale, and removed when
|
|
9699
9746
|
* the import does not produce one: only that tool's import ever writes it, so
|
|
9700
9747
|
* a setting removed from the tool's own config has to disappear here too.
|
|
9701
|
-
*
|
|
9702
|
-
*
|
|
9703
|
-
*
|
|
9704
|
-
* `permission` record inside the block merges per category. Every other
|
|
9705
|
-
* top-level key — `$schema` included — is kept unless the import names it.
|
|
9748
|
+
* Every other top-level key — `$schema` included — is kept unless the import
|
|
9749
|
+
* names it, in which case the imported value replaces it. No importer emits
|
|
9750
|
+
* another tool's override block, so those blocks are never touched here.
|
|
9706
9751
|
*/
|
|
9707
9752
|
static mergeImportedJson({ existing, imported, toolTarget }) {
|
|
9708
9753
|
const ownOverrideKey = PERMISSION_OVERRIDE_KEY_ALIASES[toolTarget] ?? toolTarget;
|
|
@@ -9714,14 +9759,7 @@ var RulesyncPermissions = class RulesyncPermissions extends RulesyncFile {
|
|
|
9714
9759
|
existing: current,
|
|
9715
9760
|
imported: value
|
|
9716
9761
|
});
|
|
9717
|
-
else
|
|
9718
|
-
const block = { ...current };
|
|
9719
|
-
for (const [field, fieldValue] of Object.entries(value)) block[field] = field === "permission" ? mergePermissionCategories({
|
|
9720
|
-
existing: block[field],
|
|
9721
|
-
imported: fieldValue
|
|
9722
|
-
}) : fieldValue;
|
|
9723
|
-
merged[key] = block;
|
|
9724
|
-
} else merged[key] = value;
|
|
9762
|
+
else merged[key] = value;
|
|
9725
9763
|
}
|
|
9726
9764
|
return merged;
|
|
9727
9765
|
}
|
|
@@ -19580,9 +19618,6 @@ var KiloCommand = class extends FrontmatterMarkdownToolCommand {
|
|
|
19580
19618
|
static getFrontmatterSchema() {
|
|
19581
19619
|
return KiloCommandFrontmatterSchema;
|
|
19582
19620
|
}
|
|
19583
|
-
getRulesyncOutputRoot() {
|
|
19584
|
-
return process.cwd();
|
|
19585
|
-
}
|
|
19586
19621
|
/**
|
|
19587
19622
|
* Refuse to write a command Kilo reserves (see
|
|
19588
19623
|
* {@link KILO_RESERVED_COMMAND_NAMES}). The name Kilo loads is the file
|
|
@@ -20871,9 +20906,6 @@ var OpenCodeCommand = class extends FrontmatterMarkdownToolCommand {
|
|
|
20871
20906
|
...Object.keys(restFields).length > 0 && { [toolTarget]: restFields }
|
|
20872
20907
|
};
|
|
20873
20908
|
}
|
|
20874
|
-
getRulesyncOutputRoot() {
|
|
20875
|
-
return process.cwd();
|
|
20876
|
-
}
|
|
20877
20909
|
/**
|
|
20878
20910
|
* Imports commands defined inline in `opencode.json` / `opencode.jsonc` (and,
|
|
20879
20911
|
* at project scope, `.opencode/opencode.json(c)`, which OpenCode merges over
|
|
@@ -52388,6 +52420,29 @@ var AmpHooks = class AmpHooks extends ToolHooks {
|
|
|
52388
52420
|
});
|
|
52389
52421
|
}
|
|
52390
52422
|
};
|
|
52423
|
+
// src/features/hooks/hook-matcher-tool-names.ts
|
|
52424
|
+
const MATCHER_TOKEN_PATTERN = /\\.|\[(?:\\.|[^\]\\])*\]|(?<=^|[^\w\\]|\\\w)[A-Za-z_]\w*(?!\w)/gs;
|
|
52425
|
+
function isMatcherName(token) {
|
|
52426
|
+
return !token.startsWith("\\") && !token.startsWith("[");
|
|
52427
|
+
}
|
|
52428
|
+
/**
|
|
52429
|
+
* The distinct whole names in a matcher that start with a capital letter, for
|
|
52430
|
+
* adapters whose runtime tool names are all lowercase: after translation, such
|
|
52431
|
+
* a name has no runtime equivalent and can never match.
|
|
52432
|
+
*/
|
|
52433
|
+
function capitalizedMatcherNames(matcher) {
|
|
52434
|
+
const names = [...matcher.matchAll(MATCHER_TOKEN_PATTERN)].map(([token]) => token).filter((token) => isMatcherName(token) && /^[A-Z]/.test(token));
|
|
52435
|
+
return [...new Set(names)];
|
|
52436
|
+
}
|
|
52437
|
+
/**
|
|
52438
|
+
* Rewrites the whole tool names inside a hook `matcher` regex through `names`,
|
|
52439
|
+
* leaving escapes, character classes and unknown names as written. Used by
|
|
52440
|
+
* adapters whose runtime tool names differ from the canonical (Claude Code)
|
|
52441
|
+
* spelling.
|
|
52442
|
+
*/
|
|
52443
|
+
function replaceMatcherNames(matcher, names) {
|
|
52444
|
+
return matcher.replace(MATCHER_TOKEN_PATTERN, (token) => isMatcherName(token) && Object.hasOwn(names, token) ? names[token] ?? token : token);
|
|
52445
|
+
}
|
|
52391
52446
|
// src/utils/object.ts
|
|
52392
52447
|
/**
|
|
52393
52448
|
* Return a shallow copy of `obj` keeping only the entries whose value is
|
|
@@ -54308,6 +54363,48 @@ const ANTIGRAVITY_FLAT_HANDLER_EVENTS = new Set([...ANTIGRAVITY_NO_MATCHER_EVENT
|
|
|
54308
54363
|
record: CANONICAL_TO_ANTIGRAVITY_EVENT_NAMES,
|
|
54309
54364
|
key: event
|
|
54310
54365
|
}) ?? event));
|
|
54366
|
+
const CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES = {
|
|
54367
|
+
Bash: "run_command",
|
|
54368
|
+
Read: "(?:read_file|view_file)",
|
|
54369
|
+
Write: "(?:write_file|write_to_file)",
|
|
54370
|
+
Edit: "(?:edit_file|replace_file_content)",
|
|
54371
|
+
WebSearch: "search_web"
|
|
54372
|
+
};
|
|
54373
|
+
const ANTIGRAVITY_TO_CANONICAL_TOOL_NAMES = {
|
|
54374
|
+
run_command: "Bash",
|
|
54375
|
+
read_file: "Read",
|
|
54376
|
+
view_file: "Read",
|
|
54377
|
+
write_file: "Write",
|
|
54378
|
+
write_to_file: "Write",
|
|
54379
|
+
edit_file: "Edit",
|
|
54380
|
+
replace_file_content: "Edit",
|
|
54381
|
+
search_web: "WebSearch"
|
|
54382
|
+
};
|
|
54383
|
+
const ANTIGRAVITY_GROUP_TO_CANONICAL_TOOL_NAMES = Object.entries(CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES).filter(([, name]) => name.startsWith("(?:")).map(([canonical, group]) => [group, canonical]);
|
|
54384
|
+
const ANTIGRAVITY_DOCUMENTED_TOOL_CANDIDATES = {
|
|
54385
|
+
Grep: "grep_search",
|
|
54386
|
+
Glob: "find_by_name",
|
|
54387
|
+
WebFetch: "read_url_content",
|
|
54388
|
+
MultiEdit: "multi_replace_file_content"
|
|
54389
|
+
};
|
|
54390
|
+
function canonicalToAntigravityMatcher({ matcher, eventName, warn }) {
|
|
54391
|
+
const translated = replaceMatcherNames(matcher, CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES);
|
|
54392
|
+
const unmatched = capitalizedMatcherNames(translated);
|
|
54393
|
+
if (unmatched.length > 0) {
|
|
54394
|
+
const candidates = unmatched.flatMap((name) => {
|
|
54395
|
+
const candidate = lookupOwn({
|
|
54396
|
+
record: ANTIGRAVITY_DOCUMENTED_TOOL_CANDIDATES,
|
|
54397
|
+
key: name
|
|
54398
|
+
});
|
|
54399
|
+
return candidate ? [`\`${candidate}\` for ${quoteValueForWarning(name)}`] : [];
|
|
54400
|
+
});
|
|
54401
|
+
warn(`Antigravity reports lowercase tool names in the hook payload (\`toolCall.name\`), and ${unmatched.map((name) => quoteValueForWarning(name)).join(", ")} in the "${eventName}" matcher ${quoteValueForWarning(matcher)} has no verified Antigravity equivalent, so it is written as-is and will likely never match. Use the tool name Antigravity reports instead` + (candidates.length > 0 ? ` (the Antigravity hooks docs list ${candidates.join(", ")}, unverified).` : "."));
|
|
54402
|
+
}
|
|
54403
|
+
return translated;
|
|
54404
|
+
}
|
|
54405
|
+
function antigravityToCanonicalMatcher(matcher) {
|
|
54406
|
+
return replaceMatcherNames(ANTIGRAVITY_GROUP_TO_CANONICAL_TOOL_NAMES.reduce((current, [group, canonical]) => current.split(group).join(canonical), matcher), ANTIGRAVITY_TO_CANONICAL_TOOL_NAMES);
|
|
54407
|
+
}
|
|
54311
54408
|
/**
|
|
54312
54409
|
* Antigravity uses a Claude-Code-like `hooks.json`. The `PreToolUse` /
|
|
54313
54410
|
* `PostToolUse` events use the matcher-based group shape, while `SessionStart`,
|
|
@@ -54321,7 +54418,11 @@ const ANTIGRAVITY_CONVERTER_CONFIG = {
|
|
|
54321
54418
|
toolToCanonicalEventNames: ANTIGRAVITY_TO_CANONICAL_EVENT_NAMES,
|
|
54322
54419
|
projectDirVar: "",
|
|
54323
54420
|
noMatcherEvents: ANTIGRAVITY_NO_MATCHER_EVENTS,
|
|
54324
|
-
supportedHookTypes: /* @__PURE__ */ new Set(["command"])
|
|
54421
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
54422
|
+
matcherTranslation: {
|
|
54423
|
+
toTool: canonicalToAntigravityMatcher,
|
|
54424
|
+
toCanonical: antigravityToCanonicalMatcher
|
|
54425
|
+
}
|
|
54325
54426
|
};
|
|
54326
54427
|
/**
|
|
54327
54428
|
* Turn the shared converter's matcher-group output into Antigravity's shape:
|
|
@@ -57354,20 +57455,6 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
|
|
|
57354
57455
|
}), null, 2) });
|
|
57355
57456
|
}
|
|
57356
57457
|
};
|
|
57357
|
-
// src/features/hooks/hook-matcher-tool-names.ts
|
|
57358
|
-
const MATCHER_TOKEN_PATTERN = /\\.|\[(?:\\.|[^\]\\])*\]|(?<=^|[^\w\\]|\\\w)[A-Za-z_]\w*(?!\w)/gs;
|
|
57359
|
-
function isMatcherName(token) {
|
|
57360
|
-
return !token.startsWith("\\") && !token.startsWith("[");
|
|
57361
|
-
}
|
|
57362
|
-
/**
|
|
57363
|
-
* Rewrites the whole tool names inside a hook `matcher` regex through `names`,
|
|
57364
|
-
* leaving escapes, character classes and unknown names as written. Used by
|
|
57365
|
-
* adapters whose runtime tool names differ from the canonical (Claude Code)
|
|
57366
|
-
* spelling.
|
|
57367
|
-
*/
|
|
57368
|
-
function replaceMatcherNames(matcher, names) {
|
|
57369
|
-
return matcher.replace(MATCHER_TOKEN_PATTERN, (token) => isMatcherName(token) && Object.hasOwn(names, token) ? names[token] ?? token : token);
|
|
57370
|
-
}
|
|
57371
57458
|
// src/features/hooks/cortexcode-hooks.ts
|
|
57372
57459
|
const CORTEXCODE_NO_MATCHER_EVENTS = /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"]);
|
|
57373
57460
|
const CANONICAL_TO_CORTEXCODE_TOOL_NAMES = {
|
|
@@ -57381,8 +57468,8 @@ const CANONICAL_TO_CORTEXCODE_TOOL_NAMES = {
|
|
|
57381
57468
|
const CORTEXCODE_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_CORTEXCODE_TOOL_NAMES);
|
|
57382
57469
|
function canonicalToCortexcodeMatcher({ matcher, eventName, warn }) {
|
|
57383
57470
|
const translated = replaceMatcherNames(matcher, CANONICAL_TO_CORTEXCODE_TOOL_NAMES);
|
|
57384
|
-
const unmatched =
|
|
57385
|
-
if (unmatched.length > 0) warn(`Cortex Code matches hook matchers case-sensitively against lowercase runtime tool names, and ${
|
|
57471
|
+
const unmatched = capitalizedMatcherNames(translated);
|
|
57472
|
+
if (unmatched.length > 0) warn(`Cortex Code matches hook matchers case-sensitively against lowercase runtime tool names, and ${unmatched.map((name) => quoteValueForWarning(name)).join(", ")} in the "${eventName}" matcher ${quoteValueForWarning(matcher)} is not a tool Cortex Code documents a runtime ID for, so it is written as-is and will likely never match. Replace it with the lowercase name Cortex Code uses for that tool, if it has one.`);
|
|
57386
57473
|
return translated;
|
|
57387
57474
|
}
|
|
57388
57475
|
function cortexcodeToCanonicalMatcher(matcher) {
|
|
@@ -58630,12 +58717,11 @@ var GooseHooks = class GooseHooks extends ToolHooks {
|
|
|
58630
58717
|
});
|
|
58631
58718
|
}
|
|
58632
58719
|
toRulesyncHooks({ logger } = {}) {
|
|
58633
|
-
const parsed = this.parseFileContentOrThrow({
|
|
58634
|
-
displayName: "Goose",
|
|
58635
|
-
parse: (fileContent) => JSON.parse(fileContent)
|
|
58636
|
-
});
|
|
58637
58720
|
const hooks = toolHooksToCanonical({
|
|
58638
|
-
hooks:
|
|
58721
|
+
hooks: this.parseFileContentOrThrow({
|
|
58722
|
+
displayName: "Goose",
|
|
58723
|
+
parse: (fileContent) => JSON.parse(fileContent)
|
|
58724
|
+
}).hooks,
|
|
58639
58725
|
converterConfig: GOOSE_CONVERTER_CONFIG,
|
|
58640
58726
|
logger
|
|
58641
58727
|
});
|
|
@@ -61902,12 +61988,14 @@ var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
|
61902
61988
|
const paths = ReasonixHooks.getSettablePaths({ global });
|
|
61903
61989
|
const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
61904
61990
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
61905
|
-
let
|
|
61991
|
+
let parsed;
|
|
61906
61992
|
try {
|
|
61907
|
-
|
|
61993
|
+
parsed = JSON.parse(existingContent);
|
|
61908
61994
|
} catch (error) {
|
|
61909
61995
|
throw new Error(`Failed to parse existing Reasonix settings at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
61910
61996
|
}
|
|
61997
|
+
if (!isPlainObject(parsed)) throw new Error(`Failed to parse existing Reasonix settings at ${filePath}: expected a mapping at the root`);
|
|
61998
|
+
const settings = parsed;
|
|
61911
61999
|
const config = rulesyncHooks.getJson();
|
|
61912
62000
|
const reasonixHooks = canonicalToReasonixHooks({
|
|
61913
62001
|
config,
|
|
@@ -62866,15 +62954,18 @@ function emittedEventsWithHook(params) {
|
|
|
62866
62954
|
}
|
|
62867
62955
|
/**
|
|
62868
62956
|
* The capabilities an adapter built on the shared hooks converter already
|
|
62869
|
-
* declares in its converter config: the events it emits
|
|
62870
|
-
* keeps
|
|
62871
|
-
*
|
|
62957
|
+
* declares in its converter config: the events it emits, the hook types it
|
|
62958
|
+
* keeps, and whether it writes the per-hook `enabled` flag. Reading them from
|
|
62959
|
+
* there keeps the processor's skip and dropped-field warnings in step with
|
|
62960
|
+
* what the converter actually writes.
|
|
62872
62961
|
*/
|
|
62873
62962
|
function converterCapabilities(config) {
|
|
62874
62963
|
if (!config.supportedHookTypes) throw new Error("Hooks converter config must declare its supportedHookTypes");
|
|
62964
|
+
const passesThroughEnabled = (config.booleanPassthroughFields ?? []).some((field) => field.canonical === "enabled");
|
|
62875
62965
|
return {
|
|
62876
62966
|
supportedEvents: config.supportedEvents,
|
|
62877
|
-
supportedHookTypes: [...config.supportedHookTypes]
|
|
62967
|
+
supportedHookTypes: [...config.supportedHookTypes],
|
|
62968
|
+
...passesThroughEnabled ? { supportsPerHookEnabled: true } : {}
|
|
62878
62969
|
};
|
|
62879
62970
|
}
|
|
62880
62971
|
const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
@@ -63496,8 +63587,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
63496
63587
|
supportsImport: true
|
|
63497
63588
|
},
|
|
63498
63589
|
...converterCapabilities(ZCODE_CONVERTER_CONFIG),
|
|
63499
|
-
supportsMatcher: true
|
|
63500
|
-
supportsPerHookEnabled: true
|
|
63590
|
+
supportsMatcher: true
|
|
63501
63591
|
}],
|
|
63502
63592
|
["zcode-plugin", {
|
|
63503
63593
|
class: ZcodePluginHooks,
|
|
@@ -63507,10 +63597,12 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
63507
63597
|
supportsImport: true
|
|
63508
63598
|
},
|
|
63509
63599
|
...converterCapabilities(ZCODE_PLUGIN_CONVERTER_CONFIG),
|
|
63510
|
-
supportsMatcher: true
|
|
63511
|
-
supportsPerHookEnabled: true
|
|
63600
|
+
supportsMatcher: true
|
|
63512
63601
|
}]
|
|
63513
63602
|
]);
|
|
63603
|
+
function targetsSupporting(capability) {
|
|
63604
|
+
return [...toolHooksFactories.entries()].filter(([, f]) => f[capability]).map(([t]) => t).join(" / ");
|
|
63605
|
+
}
|
|
63514
63606
|
const hooksProcessorToolTargets = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsProject).map(([t]) => t);
|
|
63515
63607
|
const hooksProcessorToolTargetsGlobal = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsGlobal).map(([t]) => t);
|
|
63516
63608
|
const hooksProcessorToolTargetsImportable = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsProject && f.meta.supportsImport).map(([t]) => t);
|
|
@@ -63671,7 +63763,7 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
63671
63763
|
effectiveHooks,
|
|
63672
63764
|
predicate: (def) => def.enabled === false
|
|
63673
63765
|
});
|
|
63674
|
-
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the
|
|
63766
|
+
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the ${targetsSupporting("supportsPerHookEnabled")} hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
|
|
63675
63767
|
}
|
|
63676
63768
|
if (!factory.supportsPerHookConfirm) {
|
|
63677
63769
|
const eventsWithConfirmHooks = emittedEventsWithHook({
|
|
@@ -63680,7 +63772,7 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
63680
63772
|
effectiveHooks,
|
|
63681
63773
|
predicate: (def) => def.confirm !== void 0
|
|
63682
63774
|
});
|
|
63683
|
-
if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the
|
|
63775
|
+
if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the ${targetsSupporting("supportsPerHookConfirm")} hooks formats support it): ${eventsWithConfirmHooks.join(", ")}`);
|
|
63684
63776
|
}
|
|
63685
63777
|
}
|
|
63686
63778
|
async convertToolFilesToRulesyncFiles(toolFiles) {
|
|
@@ -64943,12 +65035,7 @@ var ReasonixIgnore = class ReasonixIgnore extends ToolIgnore {
|
|
|
64943
65035
|
}
|
|
64944
65036
|
toRulesyncIgnore() {
|
|
64945
65037
|
const rulesyncPatterns = (0, es_toolkit.uniq)(this.patterns.map((entry) => readDenyPatternOf(entry)).filter((pattern) => pattern !== null && pattern.length > 0));
|
|
64946
|
-
return
|
|
64947
|
-
outputRoot: this.outputRoot,
|
|
64948
|
-
relativeDirPath: RulesyncIgnore.getSettablePaths().recommended.relativeDirPath,
|
|
64949
|
-
relativeFilePath: RulesyncIgnore.getSettablePaths().recommended.relativeFilePath,
|
|
64950
|
-
fileContent: rulesyncPatterns.join("\n")
|
|
64951
|
-
});
|
|
65038
|
+
return this.toRulesyncIgnoreDefault({ fileContent: rulesyncPatterns.join("\n") });
|
|
64952
65039
|
}
|
|
64953
65040
|
static async fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, global = false, logger }) {
|
|
64954
65041
|
await warnIfReasonixNestedProjectConfigShadowed({
|
|
@@ -78655,11 +78742,17 @@ function convertAmpToRulesync({ disable, permissions }) {
|
|
|
78655
78742
|
const assign = (tool, pattern, action) => {
|
|
78656
78743
|
if (isPrototypePollutionKey(tool)) return;
|
|
78657
78744
|
if (isPrototypePollutionKey(pattern)) return;
|
|
78658
|
-
const bucket =
|
|
78659
|
-
|
|
78745
|
+
const bucket = getOrCreateOwn({
|
|
78746
|
+
record: permission,
|
|
78747
|
+
key: tool,
|
|
78748
|
+
create: () => ({})
|
|
78749
|
+
});
|
|
78660
78750
|
if (isStricterAction({
|
|
78661
78751
|
action,
|
|
78662
|
-
existing
|
|
78752
|
+
existing: lookupOwn({
|
|
78753
|
+
record: bucket,
|
|
78754
|
+
key: pattern
|
|
78755
|
+
})
|
|
78663
78756
|
})) bucket[pattern] = action;
|
|
78664
78757
|
};
|
|
78665
78758
|
for (const tool of disable) assign(tool, "*", "deny");
|
|
@@ -80062,8 +80155,12 @@ function convertAntigravityToRulesyncPermissions({ allow, ask, deny, toCategory,
|
|
|
80062
80155
|
logger,
|
|
80063
80156
|
toolLabel
|
|
80064
80157
|
}) : target;
|
|
80065
|
-
if (
|
|
80066
|
-
|
|
80158
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
80159
|
+
getOrCreateOwn({
|
|
80160
|
+
record: permission,
|
|
80161
|
+
key: canonical,
|
|
80162
|
+
create: () => ({})
|
|
80163
|
+
})[pattern] = action;
|
|
80067
80164
|
}
|
|
80068
80165
|
};
|
|
80069
80166
|
processEntries(allow, "allow");
|
|
@@ -80131,10 +80228,16 @@ const ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES = {
|
|
|
80131
80228
|
};
|
|
80132
80229
|
function toAntigravityCliToolName(canonical) {
|
|
80133
80230
|
if (canonical.startsWith("mcp__")) return "mcp";
|
|
80134
|
-
return
|
|
80231
|
+
return lookupOwn({
|
|
80232
|
+
record: CANONICAL_TO_ANTIGRAVITY_CLI_TOOL_NAMES,
|
|
80233
|
+
key: canonical
|
|
80234
|
+
}) ?? canonical;
|
|
80135
80235
|
}
|
|
80136
80236
|
function toCanonicalToolName$9(cliName) {
|
|
80137
|
-
return
|
|
80237
|
+
return lookupOwn({
|
|
80238
|
+
record: ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES,
|
|
80239
|
+
key: cliName
|
|
80240
|
+
}) ?? cliName;
|
|
80138
80241
|
}
|
|
80139
80242
|
/**
|
|
80140
80243
|
* Permissions generator for the Google Antigravity CLI (`agy`, Antigravity 2.0).
|
|
@@ -80297,10 +80400,16 @@ const IDE_ACTION_TO_CANONICAL = {
|
|
|
80297
80400
|
};
|
|
80298
80401
|
function toIdeAction(canonical) {
|
|
80299
80402
|
if (canonical.startsWith("mcp__")) return "mcp";
|
|
80300
|
-
return
|
|
80403
|
+
return lookupOwn({
|
|
80404
|
+
record: CANONICAL_TO_IDE_ACTION,
|
|
80405
|
+
key: canonical
|
|
80406
|
+
}) ?? canonical;
|
|
80301
80407
|
}
|
|
80302
80408
|
function toCanonicalCategory$2(ideAction) {
|
|
80303
|
-
return
|
|
80409
|
+
return lookupOwn({
|
|
80410
|
+
record: IDE_ACTION_TO_CANONICAL,
|
|
80411
|
+
key: ideAction
|
|
80412
|
+
}) ?? ideAction;
|
|
80304
80413
|
}
|
|
80305
80414
|
/**
|
|
80306
80415
|
* Permissions generator for the Google Antigravity IDE (Antigravity 2.0).
|
|
@@ -81839,10 +81948,16 @@ const CANONICAL_TO_CLAUDE_TOOL_NAMES = {
|
|
|
81839
81948
|
*/
|
|
81840
81949
|
const CLAUDE_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_CLAUDE_TOOL_NAMES);
|
|
81841
81950
|
function toClaudeToolName(canonical) {
|
|
81842
|
-
return
|
|
81951
|
+
return lookupOwn({
|
|
81952
|
+
record: CANONICAL_TO_CLAUDE_TOOL_NAMES,
|
|
81953
|
+
key: canonical
|
|
81954
|
+
}) ?? canonical;
|
|
81843
81955
|
}
|
|
81844
81956
|
function toCanonicalToolName$7(claudeName) {
|
|
81845
|
-
return
|
|
81957
|
+
return lookupOwn({
|
|
81958
|
+
record: CLAUDE_TO_CANONICAL_TOOL_NAMES,
|
|
81959
|
+
key: claudeName
|
|
81960
|
+
}) ?? claudeName;
|
|
81846
81961
|
}
|
|
81847
81962
|
/**
|
|
81848
81963
|
* Merge `patch` into `base`, recursing into plain objects so a sibling key at
|
|
@@ -82814,7 +82929,10 @@ const CLAUDE_PATH_RULE_ALIASES = {
|
|
|
82814
82929
|
*/
|
|
82815
82930
|
function buildClaudePermissionEntry(toolName, pattern) {
|
|
82816
82931
|
if (pattern === "*") return toolName;
|
|
82817
|
-
return `${
|
|
82932
|
+
return `${lookupOwn({
|
|
82933
|
+
record: CLAUDE_PATH_RULE_ALIASES,
|
|
82934
|
+
key: toolName
|
|
82935
|
+
}) ?? toolName}(${pattern})`;
|
|
82818
82936
|
}
|
|
82819
82937
|
/**
|
|
82820
82938
|
* The Claude tool names the canonical config manages. Deliberately the tool
|
|
@@ -83052,8 +83170,12 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
83052
83170
|
for (const entry of entries) {
|
|
83053
83171
|
const { toolName, pattern } = parseToolPatternEntry(entry);
|
|
83054
83172
|
const canonical = toCanonicalToolName$7(toolName);
|
|
83055
|
-
if (
|
|
83056
|
-
|
|
83173
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
83174
|
+
getOrCreateOwn({
|
|
83175
|
+
record: permission,
|
|
83176
|
+
key: canonical,
|
|
83177
|
+
create: () => ({})
|
|
83178
|
+
})[pattern] = action;
|
|
83057
83179
|
}
|
|
83058
83180
|
};
|
|
83059
83181
|
processEntries(params.allow, "allow");
|
|
@@ -83635,11 +83757,17 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
83635
83757
|
for (const action of CODEBUDDY_RULE_LISTS) for (const rule of toStringArray$1(permissions[action])) {
|
|
83636
83758
|
const parsed = parseCodebuddyRule(rule);
|
|
83637
83759
|
if (parsed === null || isPrototypePollutionKey(parsed.category) || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
83638
|
-
const rules =
|
|
83639
|
-
|
|
83760
|
+
const rules = getOrCreateOwn({
|
|
83761
|
+
record: permission,
|
|
83762
|
+
key: parsed.category,
|
|
83763
|
+
create: () => ({})
|
|
83764
|
+
});
|
|
83640
83765
|
if (isStricterAction({
|
|
83641
83766
|
action,
|
|
83642
|
-
existing
|
|
83767
|
+
existing: lookupOwn({
|
|
83768
|
+
record: rules,
|
|
83769
|
+
key: parsed.pattern
|
|
83770
|
+
})
|
|
83643
83771
|
})) rules[parsed.pattern] = action;
|
|
83644
83772
|
}
|
|
83645
83773
|
const codebuddy = liftCodebuddyOverride({
|
|
@@ -84149,7 +84277,10 @@ const CODEWHALE_WRITE_RESTRICTION_TOOLS = ["write_file", "apply_patch"];
|
|
|
84149
84277
|
/** The Codewhale tools a canonical path rule is written for. */
|
|
84150
84278
|
function pathCategoryTools(category, action) {
|
|
84151
84279
|
if (category === "write" && action !== "allow") return CODEWHALE_WRITE_RESTRICTION_TOOLS;
|
|
84152
|
-
return
|
|
84280
|
+
return lookupOwn({
|
|
84281
|
+
record: CODEWHALE_PATH_CATEGORY_TOOLS,
|
|
84282
|
+
key: category
|
|
84283
|
+
}) ?? [];
|
|
84153
84284
|
}
|
|
84154
84285
|
/** The canonical path categories a Codewhale file tool's rule can come from. */
|
|
84155
84286
|
const CODEWHALE_TOOL_TO_PATH_CATEGORIES = {
|
|
@@ -84468,7 +84599,10 @@ function canonicalToCodewhaleRules({ config, preservedRules, logger }) {
|
|
|
84468
84599
|
});
|
|
84469
84600
|
for (const [category, rules] of Object.entries(config.permission)) {
|
|
84470
84601
|
if (category === "bash" || category === "*") continue;
|
|
84471
|
-
if (
|
|
84602
|
+
if (lookupOwn({
|
|
84603
|
+
record: CODEWHALE_PATH_CATEGORY_TOOLS,
|
|
84604
|
+
key: category
|
|
84605
|
+
}) === void 0) {
|
|
84472
84606
|
if (Object.keys(rules).length > 0) logger?.warn(`Codewhale permissions: skipping unsupported category "${category}"; Codewhale evaluates permission rules only for shell commands and file tools.`);
|
|
84473
84607
|
continue;
|
|
84474
84608
|
}
|
|
@@ -84578,7 +84712,10 @@ function toImportCandidates(rule) {
|
|
|
84578
84712
|
}
|
|
84579
84713
|
const pattern = pathRuleToPattern(rule);
|
|
84580
84714
|
if (pattern === null) return [];
|
|
84581
|
-
return (
|
|
84715
|
+
return (lookupOwn({
|
|
84716
|
+
record: CODEWHALE_TOOL_TO_PATH_CATEGORIES,
|
|
84717
|
+
key: rule.tool
|
|
84718
|
+
}) ?? []).map((category) => ({
|
|
84582
84719
|
category,
|
|
84583
84720
|
pattern,
|
|
84584
84721
|
rule
|
|
@@ -86186,10 +86323,17 @@ function parseCommandcodeRuleLists(permissions) {
|
|
|
86186
86323
|
rule,
|
|
86187
86324
|
category
|
|
86188
86325
|
})) fallbackLogger.warn(`Command Code matches '${rule}' in "${action}" against MCP tool names case-insensitively, but the '${category}' rule generated from it matches the server and tool names exactly as written; spell the rule the way the tool is registered if they differ.`);
|
|
86189
|
-
const rules =
|
|
86326
|
+
const rules = getOrCreateOwn({
|
|
86327
|
+
record: permission,
|
|
86328
|
+
key: category,
|
|
86329
|
+
create: () => ({})
|
|
86330
|
+
});
|
|
86190
86331
|
if (isStricterAction({
|
|
86191
86332
|
action,
|
|
86192
|
-
existing:
|
|
86333
|
+
existing: lookupOwn({
|
|
86334
|
+
record: rules,
|
|
86335
|
+
key: pattern
|
|
86336
|
+
})
|
|
86193
86337
|
})) rules[pattern] = action;
|
|
86194
86338
|
}
|
|
86195
86339
|
}
|
|
@@ -86550,9 +86694,12 @@ function convertContinuePermissionsToRulesync(lists) {
|
|
|
86550
86694
|
const parsedEntry = parseContinuePermissionEntry(entry);
|
|
86551
86695
|
if (parsedEntry === void 0) continue;
|
|
86552
86696
|
if (isPrototypePollutionKey(parsedEntry.toolName) || isPrototypePollutionKey(parsedEntry.pattern)) continue;
|
|
86553
|
-
const
|
|
86554
|
-
|
|
86555
|
-
|
|
86697
|
+
const rules = getOrCreateOwn({
|
|
86698
|
+
record: permission,
|
|
86699
|
+
key: toCanonicalToolName$6(parsedEntry.toolName),
|
|
86700
|
+
create: () => ({})
|
|
86701
|
+
});
|
|
86702
|
+
if (!Object.hasOwn(rules, parsedEntry.pattern)) rules[parsedEntry.pattern] = action;
|
|
86556
86703
|
}
|
|
86557
86704
|
}
|
|
86558
86705
|
return { permission };
|
|
@@ -87181,7 +87328,9 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
87181
87328
|
const settings = this.parseSettings();
|
|
87182
87329
|
const rules = {};
|
|
87183
87330
|
for (const pattern of toUrlList(settings[DENIED_URLS_KEY])) rules[pattern] = "deny";
|
|
87184
|
-
if (this.global)
|
|
87331
|
+
if (this.global) {
|
|
87332
|
+
for (const pattern of toUrlList(settings[ALLOWED_URLS_KEY])) if (!Object.hasOwn(rules, pattern)) rules[pattern] = "allow";
|
|
87333
|
+
}
|
|
87185
87334
|
const permission = Object.keys(rules).length > 0 ? { [WEBFETCH_CATEGORY$1]: rules } : {};
|
|
87186
87335
|
const defaultPermissionMode = this.global ? settings[DEFAULT_PERMISSION_MODE_KEY] : void 0;
|
|
87187
87336
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
@@ -87528,7 +87677,10 @@ function isMcpScopedCategory(canonical) {
|
|
|
87528
87677
|
}
|
|
87529
87678
|
function toCursorType(canonical) {
|
|
87530
87679
|
if (isMcpScopedCategory(canonical)) return "Mcp";
|
|
87531
|
-
return
|
|
87680
|
+
return lookupOwn({
|
|
87681
|
+
record: CANONICAL_TO_CURSOR_TYPE,
|
|
87682
|
+
key: canonical
|
|
87683
|
+
}) ?? canonical;
|
|
87532
87684
|
}
|
|
87533
87685
|
/**
|
|
87534
87686
|
* For per-tool MCP canonical categories like `mcp__puppeteer__navigate`, the
|
|
@@ -87560,9 +87712,15 @@ function toCanonicalCategory$1(cursorType, pattern) {
|
|
|
87560
87712
|
const tool = match[2] ?? "*";
|
|
87561
87713
|
return `${MCP_CATEGORY_PREFIX}${server}__${tool}`;
|
|
87562
87714
|
}
|
|
87563
|
-
return
|
|
87715
|
+
return lookupOwn({
|
|
87716
|
+
record: CURSOR_TYPE_TO_CANONICAL,
|
|
87717
|
+
key: cursorType
|
|
87718
|
+
}) ?? cursorType.toLowerCase();
|
|
87564
87719
|
}
|
|
87565
|
-
return
|
|
87720
|
+
return lookupOwn({
|
|
87721
|
+
record: CURSOR_TYPE_TO_CANONICAL,
|
|
87722
|
+
key: cursorType
|
|
87723
|
+
}) ?? cursorType.toLowerCase();
|
|
87566
87724
|
}
|
|
87567
87725
|
/**
|
|
87568
87726
|
* Narrow `JSON.parse` output to a plain object before treating it as a Cursor
|
|
@@ -87807,9 +87965,13 @@ function convertCursorToRulesyncPermissions(params) {
|
|
|
87807
87965
|
for (const entry of entries) {
|
|
87808
87966
|
const { toolName: type, pattern } = parseToolPatternEntry(entry);
|
|
87809
87967
|
const canonical = toCanonicalCategory$1(type, pattern);
|
|
87810
|
-
if (!permission[canonical]) permission[canonical] = {};
|
|
87811
87968
|
const canonicalPattern = type === "Mcp" && canonical.startsWith("mcp__") ? "*" : pattern;
|
|
87812
|
-
|
|
87969
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(canonicalPattern)) continue;
|
|
87970
|
+
getOrCreateOwn({
|
|
87971
|
+
record: permission,
|
|
87972
|
+
key: canonical,
|
|
87973
|
+
create: () => ({})
|
|
87974
|
+
})[canonicalPattern] = action;
|
|
87813
87975
|
}
|
|
87814
87976
|
};
|
|
87815
87977
|
processEntries(params.allow, "allow");
|
|
@@ -88736,10 +88898,16 @@ const DEVIN_SCOPE_TO_CANONICAL = {
|
|
|
88736
88898
|
*/
|
|
88737
88899
|
const DEVIN_BARE_ONLY_SCOPES = /* @__PURE__ */ new Set(["web_search"]);
|
|
88738
88900
|
function toDevinScope(canonical) {
|
|
88739
|
-
return
|
|
88901
|
+
return lookupOwn({
|
|
88902
|
+
record: CANONICAL_TO_DEVIN_SCOPE,
|
|
88903
|
+
key: canonical
|
|
88904
|
+
}) ?? canonical;
|
|
88740
88905
|
}
|
|
88741
88906
|
function toCanonicalCategory(devinScope) {
|
|
88742
|
-
return
|
|
88907
|
+
return lookupOwn({
|
|
88908
|
+
record: DEVIN_SCOPE_TO_CANONICAL,
|
|
88909
|
+
key: devinScope
|
|
88910
|
+
}) ?? devinScope;
|
|
88743
88911
|
}
|
|
88744
88912
|
function asDevinRecord(value) {
|
|
88745
88913
|
return isRecord(value) ? { ...value } : {};
|
|
@@ -89091,7 +89259,11 @@ function convertDevinToRulesyncPermissions(params) {
|
|
|
89091
89259
|
const { toolName: scope, pattern } = parseToolPatternEntry(entry);
|
|
89092
89260
|
if (isPrototypePollutionKey(scope) || isPrototypePollutionKey(pattern)) continue;
|
|
89093
89261
|
const canonical = toCanonicalCategory(scope);
|
|
89094
|
-
(
|
|
89262
|
+
getOrCreateOwn({
|
|
89263
|
+
record: permission,
|
|
89264
|
+
key: canonical,
|
|
89265
|
+
create: () => ({})
|
|
89266
|
+
})[pattern] = action;
|
|
89095
89267
|
}
|
|
89096
89268
|
};
|
|
89097
89269
|
processEntries(params.allow, "allow");
|
|
@@ -89397,7 +89569,10 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
89397
89569
|
};
|
|
89398
89570
|
const assigned = /* @__PURE__ */ new Map();
|
|
89399
89571
|
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
89400
|
-
const toolName =
|
|
89572
|
+
const toolName = lookupOwn({
|
|
89573
|
+
record: RULESYNC_TO_GOOSE_TOOL_NAME,
|
|
89574
|
+
key: category
|
|
89575
|
+
}) ?? category;
|
|
89401
89576
|
for (const [pattern, action] of Object.entries(rules)) if (pattern !== "*") logger?.warn(`Goose permission.yaml lists whole tool names, so the per-pattern "${action}" rule for "${category}" (pattern "${pattern}") cannot be expressed and was skipped.`);
|
|
89402
89577
|
const { action, downgraded } = failSafeCatchAllAction({
|
|
89403
89578
|
rules,
|
|
@@ -89429,9 +89604,16 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
|
89429
89604
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
89430
89605
|
const toolNames = isStringArray(userPermission[key]) ? userPermission[key] : [];
|
|
89431
89606
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
89432
|
-
for (const toolName of toolNames) for (const category of
|
|
89433
|
-
|
|
89434
|
-
|
|
89607
|
+
for (const toolName of toolNames) for (const category of lookupOwn({
|
|
89608
|
+
record: GOOSE_TO_RULESYNC_TOOL_NAMES,
|
|
89609
|
+
key: toolName
|
|
89610
|
+
}) ?? [toolName]) {
|
|
89611
|
+
if (isPrototypePollutionKey(category)) continue;
|
|
89612
|
+
getOrCreateOwn({
|
|
89613
|
+
record: permission,
|
|
89614
|
+
key: category,
|
|
89615
|
+
create: () => ({})
|
|
89616
|
+
})["*"] = action;
|
|
89435
89617
|
}
|
|
89436
89618
|
}
|
|
89437
89619
|
return { permission };
|
|
@@ -89512,7 +89694,10 @@ function buildGrokEntry(category, pattern) {
|
|
|
89512
89694
|
return remainder.length > 0 ? `${GROK_MCP_TOOL}(${remainder})` : GROK_MCP_TOOL;
|
|
89513
89695
|
}
|
|
89514
89696
|
if (category === "mcp") return pattern === "*" || pattern === "" ? GROK_MCP_TOOL : `${GROK_MCP_TOOL}(${pattern})`;
|
|
89515
|
-
const tool =
|
|
89697
|
+
const tool = lookupOwn({
|
|
89698
|
+
record: CATEGORY_TO_GROK_TOOL,
|
|
89699
|
+
key: category
|
|
89700
|
+
});
|
|
89516
89701
|
if (tool === void 0) return null;
|
|
89517
89702
|
return pattern === "*" || pattern === "" ? tool : `${tool}(${toGrokPathPattern(category, pattern)})`;
|
|
89518
89703
|
}
|
|
@@ -89546,7 +89731,10 @@ function parseGrokEntry(entry) {
|
|
|
89546
89731
|
category: "mcp",
|
|
89547
89732
|
pattern: "*"
|
|
89548
89733
|
};
|
|
89549
|
-
const category =
|
|
89734
|
+
const category = lookupOwn({
|
|
89735
|
+
record: GROK_TOOL_TO_CATEGORY,
|
|
89736
|
+
key: tool
|
|
89737
|
+
});
|
|
89550
89738
|
if (category === void 0) return null;
|
|
89551
89739
|
return {
|
|
89552
89740
|
category,
|
|
@@ -89867,11 +90055,17 @@ function parseGrokPermissionArrays(permission) {
|
|
|
89867
90055
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
89868
90056
|
const result = {};
|
|
89869
90057
|
const record = ({ category, pattern, action }) => {
|
|
89870
|
-
const bucket =
|
|
89871
|
-
|
|
90058
|
+
const bucket = getOrCreateOwn({
|
|
90059
|
+
record: result,
|
|
90060
|
+
key: category,
|
|
90061
|
+
create: () => ({})
|
|
90062
|
+
});
|
|
89872
90063
|
if (isStricterAction({
|
|
89873
90064
|
action,
|
|
89874
|
-
existing
|
|
90065
|
+
existing: lookupOwn({
|
|
90066
|
+
record: bucket,
|
|
90067
|
+
key: pattern
|
|
90068
|
+
})
|
|
89875
90069
|
})) bucket[pattern] = action;
|
|
89876
90070
|
};
|
|
89877
90071
|
const applyEntries = (entries, action) => {
|
|
@@ -89928,7 +90122,11 @@ function deleteRulesByAction(rules, action) {
|
|
|
89928
90122
|
for (const [pattern, currentAction] of Object.entries(rules)) if (currentAction === action) delete rules[pattern];
|
|
89929
90123
|
}
|
|
89930
90124
|
function ensureCategory(permission, category) {
|
|
89931
|
-
return
|
|
90125
|
+
return getOrCreateOwn({
|
|
90126
|
+
record: permission,
|
|
90127
|
+
key: category,
|
|
90128
|
+
create: () => ({})
|
|
90129
|
+
});
|
|
89932
90130
|
}
|
|
89933
90131
|
function removeEmptyCategories(permission) {
|
|
89934
90132
|
for (const [category, rules] of Object.entries(permission)) if (Object.keys(rules).length === 0) delete permission[category];
|
|
@@ -90109,6 +90307,22 @@ function unmanagedMcpAllowlistEntries({ existing, previousPermission, nextPermis
|
|
|
90109
90307
|
return category === null || !Object.hasOwn(previousPermission, category) && !Object.hasOwn(nextPermission, category);
|
|
90110
90308
|
});
|
|
90111
90309
|
}
|
|
90310
|
+
/** Hermes's override key in the canonical document (`hermes`). */
|
|
90311
|
+
const HERMES_OVERRIDE_KEY = PERMISSION_OVERRIDE_KEY_ALIASES.hermesagent ?? "hermesagent";
|
|
90312
|
+
/**
|
|
90313
|
+
* The part of a canonical permissions document Hermes keeps as provenance: the
|
|
90314
|
+
* `$schema`, the canonical `permission` block, and Hermes's own override.
|
|
90315
|
+
*/
|
|
90316
|
+
function hermesProvenanceOf(permissions) {
|
|
90317
|
+
const source = permissions;
|
|
90318
|
+
const provenance = {};
|
|
90319
|
+
for (const key of [
|
|
90320
|
+
"$schema",
|
|
90321
|
+
"permission",
|
|
90322
|
+
HERMES_OVERRIDE_KEY
|
|
90323
|
+
]) if (source[key] !== void 0) provenance[key] = source[key];
|
|
90324
|
+
return provenance;
|
|
90325
|
+
}
|
|
90112
90326
|
/** The validated canonical `permission` block of a config's `permissions.rulesync` blob. */
|
|
90113
90327
|
function canonicalPermissionOf(config) {
|
|
90114
90328
|
const parsed = RulesyncPermissionsFileSchema.safeParse({ permission: provenancePermissionOf(config) });
|
|
@@ -90276,10 +90490,10 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
90276
90490
|
patterns: websiteBlocklist.enabled === true && isStringArray(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
90277
90491
|
});
|
|
90278
90492
|
removeEmptyCategories(permission);
|
|
90279
|
-
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
90280
90493
|
const hermes = buildHermesOverride(config, provenance);
|
|
90494
|
+
const { $schema } = provenance;
|
|
90281
90495
|
const imported = {
|
|
90282
|
-
|
|
90496
|
+
...$schema !== void 0 && { $schema },
|
|
90283
90497
|
permission,
|
|
90284
90498
|
...Object.keys(hermes).length > 0 && { hermes }
|
|
90285
90499
|
};
|
|
@@ -90337,7 +90551,7 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
90337
90551
|
base: config,
|
|
90338
90552
|
patch: permissions.hermes
|
|
90339
90553
|
});
|
|
90340
|
-
config.permissions = { rulesync: permissions };
|
|
90554
|
+
config.permissions = { rulesync: hermesProvenanceOf(permissions) };
|
|
90341
90555
|
return new HermesagentPermissions({
|
|
90342
90556
|
outputRoot,
|
|
90343
90557
|
fileContent: stringifySharedConfig({
|
|
@@ -90571,7 +90785,10 @@ var JuniePermissions = class JuniePermissions extends ToolPermissions {
|
|
|
90571
90785
|
function convertRulesyncToJunieRules({ config, logger, existingRules, overrideSecretFile, overrideRuleDefaults }) {
|
|
90572
90786
|
const ruleLists = {};
|
|
90573
90787
|
for (const [category, patterns] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
90574
|
-
const group =
|
|
90788
|
+
const group = lookupOwn({
|
|
90789
|
+
record: CANONICAL_TO_JUNIE_GROUP,
|
|
90790
|
+
key: category
|
|
90791
|
+
});
|
|
90575
90792
|
if (!group) {
|
|
90576
90793
|
if (Object.keys(patterns).length > 0) logger?.warn(`Junie allowlist only models executables/fileEditing/mcpTools/readOutsideProject (canonical bash/edit/write/read/mcp); '${category}' rules cannot be represented and were skipped.`);
|
|
90577
90794
|
continue;
|
|
@@ -90969,7 +91186,10 @@ const CATEGORY_TO_KIMI_CODE_TOOL = {
|
|
|
90969
91186
|
function buildKimiCodePattern(category, pattern) {
|
|
90970
91187
|
if (category.startsWith("mcp__")) return pattern === "*" || pattern === "" ? category : null;
|
|
90971
91188
|
if (category === "mcp") return pattern === "*" || pattern === "" ? "mcp__*" : `mcp__${pattern}`;
|
|
90972
|
-
const tool =
|
|
91189
|
+
const tool = lookupOwn({
|
|
91190
|
+
record: CATEGORY_TO_KIMI_CODE_TOOL,
|
|
91191
|
+
key: category
|
|
91192
|
+
});
|
|
90973
91193
|
if (!tool) return null;
|
|
90974
91194
|
return pattern === "*" || pattern === "" ? tool : `${tool}(${pattern})`;
|
|
90975
91195
|
}
|
|
@@ -91065,7 +91285,10 @@ function canonicalToKimiCodeRules({ config, logger }) {
|
|
|
91065
91285
|
const canonicalRules = [];
|
|
91066
91286
|
const overrideRules = config["kimi-code"]?.rules ?? [];
|
|
91067
91287
|
for (const [category, rules] of Object.entries(config.permission)) {
|
|
91068
|
-
if (!(category === "mcp" || category.startsWith("mcp__") ||
|
|
91288
|
+
if (!(category === "mcp" || category.startsWith("mcp__") || lookupOwn({
|
|
91289
|
+
record: CATEGORY_TO_KIMI_CODE_TOOL,
|
|
91290
|
+
key: category
|
|
91291
|
+
}) !== void 0)) {
|
|
91069
91292
|
if (Object.keys(rules).length > 0) logger?.warn(`Kimi Code permissions: skipping unsupported category "${category}".`);
|
|
91070
91293
|
continue;
|
|
91071
91294
|
}
|
|
@@ -91882,9 +92105,16 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
91882
92105
|
if (!isImportableRule(rule) || !isRecord(rule) || !isPermissionAction(rule.effect)) continue;
|
|
91883
92106
|
const category = KIRO_CAPABILITY_TO_CANONICAL[rule.capability];
|
|
91884
92107
|
const patterns = rule.match === void 0 ? ["*"] : isStringArray(rule.match) ? rule.match : [];
|
|
91885
|
-
const categoryRules =
|
|
92108
|
+
const categoryRules = getOrCreateOwn({
|
|
92109
|
+
record: permission,
|
|
92110
|
+
key: category,
|
|
92111
|
+
create: () => ({})
|
|
92112
|
+
});
|
|
91886
92113
|
for (const pattern of patterns) {
|
|
91887
|
-
const previous =
|
|
92114
|
+
const previous = lookupOwn({
|
|
92115
|
+
record: categoryRules,
|
|
92116
|
+
key: pattern
|
|
92117
|
+
});
|
|
91888
92118
|
if (isStricterAction({
|
|
91889
92119
|
action: rule.effect,
|
|
91890
92120
|
existing: previous
|
|
@@ -91902,7 +92132,10 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
91902
92132
|
function buildKiroCapabilityRules({ config, logger }) {
|
|
91903
92133
|
const buckets = /* @__PURE__ */ new Map();
|
|
91904
92134
|
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
91905
|
-
const capability =
|
|
92135
|
+
const capability = lookupOwn({
|
|
92136
|
+
record: CANONICAL_TO_KIRO_CAPABILITY,
|
|
92137
|
+
key: category
|
|
92138
|
+
});
|
|
91906
92139
|
if (capability === void 0) {
|
|
91907
92140
|
logger?.warn(`Kiro permissions.yaml has no capability for category: ${category}. Skipping.`);
|
|
91908
92141
|
continue;
|
|
@@ -92062,7 +92295,10 @@ function toLettacodeBashPayload({ pattern, action, logger }) {
|
|
|
92062
92295
|
return prefix === "" ? "" : `${prefix}${PREFIX_SUFFIX}`;
|
|
92063
92296
|
}
|
|
92064
92297
|
function toLettacodeRule({ category, pattern, action, logger }) {
|
|
92065
|
-
const tool =
|
|
92298
|
+
const tool = lookupOwn({
|
|
92299
|
+
record: CATEGORY_TO_LETTACODE_TOOL,
|
|
92300
|
+
key: category
|
|
92301
|
+
});
|
|
92066
92302
|
if (tool === void 0) return null;
|
|
92067
92303
|
const payload = category === "bash" ? toLettacodeBashPayload({
|
|
92068
92304
|
pattern,
|
|
@@ -92217,11 +92453,17 @@ var LettacodePermissions = class LettacodePermissions extends ToolPermissions {
|
|
|
92217
92453
|
for (const [key, action] of Object.entries(LIST_TO_ACTION)) for (const rule of toStringArray$1(permissions[key])) {
|
|
92218
92454
|
const parsed = fromLettacodeRule(rule);
|
|
92219
92455
|
if (parsed === null || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
92220
|
-
const rules =
|
|
92221
|
-
|
|
92456
|
+
const rules = getOrCreateOwn({
|
|
92457
|
+
record: permission,
|
|
92458
|
+
key: parsed.category,
|
|
92459
|
+
create: () => ({})
|
|
92460
|
+
});
|
|
92222
92461
|
if (isStricterAction({
|
|
92223
92462
|
action,
|
|
92224
|
-
existing
|
|
92463
|
+
existing: lookupOwn({
|
|
92464
|
+
record: rules,
|
|
92465
|
+
key: parsed.pattern
|
|
92466
|
+
})
|
|
92225
92467
|
})) rules[parsed.pattern] = action;
|
|
92226
92468
|
}
|
|
92227
92469
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission }, null, 2) });
|
|
@@ -92408,7 +92650,13 @@ function getOpencodeMcpServerNames(mcp) {
|
|
|
92408
92650
|
return isRecord(lowered) ? Object.keys(lowered) : [];
|
|
92409
92651
|
}
|
|
92410
92652
|
function toOpencodePermissionKey(canonical) {
|
|
92411
|
-
return
|
|
92653
|
+
return lookupOwn({
|
|
92654
|
+
record: CANONICAL_TO_OPENCODE_PERMISSION_KEYS,
|
|
92655
|
+
key: canonical
|
|
92656
|
+
}) ?? lookupOwn({
|
|
92657
|
+
record: CANONICAL_TO_OPENCODE_FOLDED_PERMISSION_KEYS,
|
|
92658
|
+
key: canonical
|
|
92659
|
+
}) ?? canonical;
|
|
92412
92660
|
}
|
|
92413
92661
|
/**
|
|
92414
92662
|
* Merge two canonical pattern maps that land on the same OpenCode key.
|
|
@@ -92432,7 +92680,10 @@ function mergePatternRules(base, incoming) {
|
|
|
92432
92680
|
return Object.fromEntries(Object.entries(merged).toSorted(([, a], [, b]) => PERMISSION_ACTION_PRIORITY[a] - PERMISSION_ACTION_PRIORITY[b]));
|
|
92433
92681
|
}
|
|
92434
92682
|
function toCanonicalPermissionKey(opencodeKey) {
|
|
92435
|
-
return
|
|
92683
|
+
return lookupOwn({
|
|
92684
|
+
record: OPENCODE_TO_CANONICAL_PERMISSION_KEYS,
|
|
92685
|
+
key: opencodeKey
|
|
92686
|
+
}) ?? opencodeKey;
|
|
92436
92687
|
}
|
|
92437
92688
|
function isSharedPermissionCategory(category) {
|
|
92438
92689
|
return category === "*" || CANONICAL_PERMISSION_CATEGORIES$1.has(category) || category.startsWith("mcp__");
|
|
@@ -93714,7 +93965,14 @@ function importPoolTool({ toolConfig, bucket }) {
|
|
|
93714
93965
|
const denied = isStringArray(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
|
|
93715
93966
|
const allowed = isStringArray(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
|
|
93716
93967
|
for (const pattern of denied.filter(isUsablePattern)) bucket[pattern] = "deny";
|
|
93717
|
-
for (const pattern of allowed.filter(isUsablePattern)) bucket[pattern]
|
|
93968
|
+
for (const pattern of allowed.filter(isUsablePattern)) if (!Object.hasOwn(bucket, pattern)) bucket[pattern] = "allow";
|
|
93969
|
+
}
|
|
93970
|
+
/**
|
|
93971
|
+
* Record `allow` for a path no deny has claimed. The check is on own
|
|
93972
|
+
* properties, so a path such as `toString` is not mistaken for a stored rule.
|
|
93973
|
+
*/
|
|
93974
|
+
function allowUnlessSet(bucket, path) {
|
|
93975
|
+
if (!Object.hasOwn(bucket, path)) bucket[path] = "allow";
|
|
93718
93976
|
}
|
|
93719
93977
|
/**
|
|
93720
93978
|
* Convert Pool's `tools` and `paths` blocks back into a rulesync config.
|
|
@@ -93749,8 +94007,8 @@ function convertPoolToRulesync({ tools, paths }) {
|
|
|
93749
94007
|
for (const entry of allowEntries) {
|
|
93750
94008
|
const path = pathOfEntry(entry);
|
|
93751
94009
|
if (path === void 0) continue;
|
|
93752
|
-
bucketFor(READ_CATEGORY)
|
|
93753
|
-
if (isRecord(entry) && entry[POOL_WRITE_KEY] === true) for (const category of WRITE_CATEGORIES) bucketFor(category)
|
|
94010
|
+
allowUnlessSet(bucketFor(READ_CATEGORY), path);
|
|
94011
|
+
if (isRecord(entry) && entry[POOL_WRITE_KEY] === true) for (const category of WRITE_CATEGORIES) allowUnlessSet(bucketFor(category), path);
|
|
93754
94012
|
}
|
|
93755
94013
|
return { permission };
|
|
93756
94014
|
}
|
|
@@ -93879,7 +94137,10 @@ function toQoderRule({ category, pattern, logger }) {
|
|
|
93879
94137
|
logger?.warn(`Qoder permissions: skipping the '${category}' rule '${pattern}' — Qoder matches MCP tools by name only.`);
|
|
93880
94138
|
return null;
|
|
93881
94139
|
}
|
|
93882
|
-
const tool =
|
|
94140
|
+
const tool = lookupOwn({
|
|
94141
|
+
record: CATEGORY_TO_QODER_TOOL,
|
|
94142
|
+
key: category
|
|
94143
|
+
});
|
|
93883
94144
|
if (tool === void 0) return null;
|
|
93884
94145
|
if (pattern === "*") return tool;
|
|
93885
94146
|
return `${QODER_PATH_RULE_ALIASES[tool] ?? tool}(${pattern})`;
|
|
@@ -94165,11 +94426,17 @@ var QoderPermissions = class QoderPermissions extends ToolPermissions {
|
|
|
94165
94426
|
for (const action of ACTIONS) for (const rule of toStringArray$1(permissions[action])) {
|
|
94166
94427
|
const parsed = fromQoderRule(rule);
|
|
94167
94428
|
if (parsed === null || isPrototypePollutionKey(parsed.category) || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
94168
|
-
const rules =
|
|
94169
|
-
|
|
94429
|
+
const rules = getOrCreateOwn({
|
|
94430
|
+
record: permission,
|
|
94431
|
+
key: parsed.category,
|
|
94432
|
+
create: () => ({})
|
|
94433
|
+
});
|
|
94170
94434
|
if (isStricterAction({
|
|
94171
94435
|
action,
|
|
94172
|
-
existing
|
|
94436
|
+
existing: lookupOwn({
|
|
94437
|
+
record: rules,
|
|
94438
|
+
key: parsed.pattern
|
|
94439
|
+
})
|
|
94173
94440
|
})) rules[parsed.pattern] = action;
|
|
94174
94441
|
}
|
|
94175
94442
|
const outputStyle = settings[QODER_OUTPUT_STYLE_KEY];
|
|
@@ -94233,10 +94500,16 @@ const CANONICAL_TO_QWEN_TOOL_NAMES = {
|
|
|
94233
94500
|
};
|
|
94234
94501
|
const QWEN_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_QWEN_TOOL_NAMES);
|
|
94235
94502
|
function toQwenToolName(canonical) {
|
|
94236
|
-
return
|
|
94503
|
+
return lookupOwn({
|
|
94504
|
+
record: CANONICAL_TO_QWEN_TOOL_NAMES,
|
|
94505
|
+
key: canonical
|
|
94506
|
+
}) ?? canonical;
|
|
94237
94507
|
}
|
|
94238
94508
|
function toCanonicalToolName$3(qwenName) {
|
|
94239
|
-
return
|
|
94509
|
+
return lookupOwn({
|
|
94510
|
+
record: QWEN_TO_CANONICAL_TOOL_NAMES,
|
|
94511
|
+
key: qwenName
|
|
94512
|
+
}) ?? qwenName;
|
|
94240
94513
|
}
|
|
94241
94514
|
function parseQwenPermissionEntry(entry, options = {}) {
|
|
94242
94515
|
const parenIndex = entry.indexOf("(");
|
|
@@ -94852,15 +95125,23 @@ function convertQwenToRulesyncPermissions(params) {
|
|
|
94852
95125
|
if (!parsed.ok) {
|
|
94853
95126
|
if (action === "deny") {
|
|
94854
95127
|
const canonical = toCanonicalToolName$3(parsed.toolName);
|
|
94855
|
-
if (
|
|
94856
|
-
|
|
95128
|
+
if (isPrototypePollutionKey(canonical)) continue;
|
|
95129
|
+
getOrCreateOwn({
|
|
95130
|
+
record: permission,
|
|
95131
|
+
key: canonical,
|
|
95132
|
+
create: () => ({})
|
|
95133
|
+
})["*"] = action;
|
|
94857
95134
|
}
|
|
94858
95135
|
continue;
|
|
94859
95136
|
}
|
|
94860
95137
|
const { toolName, pattern } = parsed;
|
|
94861
95138
|
const canonical = toCanonicalToolName$3(toolName);
|
|
94862
|
-
if (
|
|
94863
|
-
|
|
95139
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
95140
|
+
getOrCreateOwn({
|
|
95141
|
+
record: permission,
|
|
95142
|
+
key: canonical,
|
|
95143
|
+
create: () => ({})
|
|
95144
|
+
})[pattern] = action;
|
|
94864
95145
|
}
|
|
94865
95146
|
};
|
|
94866
95147
|
processEntries(params.allow, "allow");
|
|
@@ -94924,15 +95205,24 @@ const REASONIX_TO_CANONICAL_TOOL_NAMES = {
|
|
|
94924
95205
|
};
|
|
94925
95206
|
/** Every Reasonix tool name a canonical category owns on generate. */
|
|
94926
95207
|
function reasonixToolNamesOf(canonical) {
|
|
94927
|
-
const legacy =
|
|
95208
|
+
const legacy = lookupOwn({
|
|
95209
|
+
record: CANONICAL_TO_LEGACY_REASONIX_TOOL_NAMES,
|
|
95210
|
+
key: canonical
|
|
95211
|
+
});
|
|
94928
95212
|
const current = toReasonixToolName(canonical);
|
|
94929
95213
|
return legacy === void 0 ? [current] : [current, legacy];
|
|
94930
95214
|
}
|
|
94931
95215
|
function toReasonixToolName(canonical) {
|
|
94932
|
-
return
|
|
95216
|
+
return lookupOwn({
|
|
95217
|
+
record: CANONICAL_TO_REASONIX_TOOL_NAMES,
|
|
95218
|
+
key: canonical
|
|
95219
|
+
}) ?? canonical;
|
|
94933
95220
|
}
|
|
94934
95221
|
function toCanonicalToolName$2(reasonixName) {
|
|
94935
|
-
return
|
|
95222
|
+
return lookupOwn({
|
|
95223
|
+
record: REASONIX_TO_CANONICAL_TOOL_NAMES,
|
|
95224
|
+
key: reasonixName
|
|
95225
|
+
}) ?? reasonixName;
|
|
94936
95226
|
}
|
|
94937
95227
|
/**
|
|
94938
95228
|
* Whether an entry uses the first-class `Tool=<literal>` exact-command form
|
|
@@ -95555,8 +95845,12 @@ function convertReasonixToRulesyncPermissions(params) {
|
|
|
95555
95845
|
for (const entry of entries) {
|
|
95556
95846
|
const { toolName, pattern } = parseToolPatternEntry(entry);
|
|
95557
95847
|
const canonical = toCanonicalToolName$2(toolName);
|
|
95558
|
-
if (
|
|
95559
|
-
|
|
95848
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
95849
|
+
getOrCreateOwn({
|
|
95850
|
+
record: permission,
|
|
95851
|
+
key: canonical,
|
|
95852
|
+
create: () => ({})
|
|
95853
|
+
})[pattern] = action;
|
|
95560
95854
|
}
|
|
95561
95855
|
};
|
|
95562
95856
|
processEntries(params.allow, "allow");
|
|
@@ -96108,7 +96402,10 @@ function convertRulesyncToRovodevToolPermissions({ config, logger }) {
|
|
|
96108
96402
|
if (bash) toolPermissions.bash = bash;
|
|
96109
96403
|
continue;
|
|
96110
96404
|
}
|
|
96111
|
-
const toolKeys =
|
|
96405
|
+
const toolKeys = lookupOwn({
|
|
96406
|
+
record: CATEGORY_TO_TOOL_KEYS,
|
|
96407
|
+
key: category
|
|
96408
|
+
});
|
|
96112
96409
|
if (!toolKeys) {
|
|
96113
96410
|
logger?.warn(`Rovo Dev permissions have no target for the "${category}" category. Skipping it.`);
|
|
96114
96411
|
markSharedToolKeyRestriction({
|
|
@@ -96256,7 +96553,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
96256
96553
|
}
|
|
96257
96554
|
if (isStringArray(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
96258
96555
|
permission.read ??= {};
|
|
96259
|
-
permission.read[path]
|
|
96556
|
+
if (!Object.hasOwn(permission.read, path)) permission.read[path] = "allow";
|
|
96260
96557
|
}
|
|
96261
96558
|
return { permission };
|
|
96262
96559
|
}
|
|
@@ -97993,11 +98290,17 @@ var TraePermissions = class TraePermissions extends ToolPermissions {
|
|
|
97993
98290
|
const permission = {};
|
|
97994
98291
|
const put = (category, pattern, action) => {
|
|
97995
98292
|
if (isPrototypePollutionKey(category) || isPrototypePollutionKey(pattern)) return;
|
|
97996
|
-
const rules =
|
|
97997
|
-
|
|
98293
|
+
const rules = getOrCreateOwn({
|
|
98294
|
+
record: permission,
|
|
98295
|
+
key: category,
|
|
98296
|
+
create: () => ({})
|
|
98297
|
+
});
|
|
97998
98298
|
if (isStricterAction({
|
|
97999
98299
|
action,
|
|
98000
|
-
existing
|
|
98300
|
+
existing: lookupOwn({
|
|
98301
|
+
record: rules,
|
|
98302
|
+
key: pattern
|
|
98303
|
+
})
|
|
98001
98304
|
})) rules[pattern] = action;
|
|
98002
98305
|
};
|
|
98003
98306
|
for (const [key, value] of Object.entries(readRecord(customProfiles, COMMAND_RULES_PATH))) {
|
|
@@ -100340,7 +100643,10 @@ function toZedToolName(canonical) {
|
|
|
100340
100643
|
const address = toolParts.length > 0 ? `${server}:${toolParts.join("__")}` : server ?? "";
|
|
100341
100644
|
return `${MCP_ZED_PREFIX}${address}`;
|
|
100342
100645
|
}
|
|
100343
|
-
return
|
|
100646
|
+
return lookupOwn({
|
|
100647
|
+
record: CANONICAL_TO_ZED_TOOL_NAMES,
|
|
100648
|
+
key: canonical
|
|
100649
|
+
}) ?? canonical;
|
|
100344
100650
|
}
|
|
100345
100651
|
function toCanonicalToolName(zedName) {
|
|
100346
100652
|
if (zedName.startsWith(MCP_ZED_PREFIX)) {
|
|
@@ -100348,7 +100654,10 @@ function toCanonicalToolName(zedName) {
|
|
|
100348
100654
|
const address = toolParts.length > 0 ? `${server}__${toolParts.join(":")}` : server ?? "";
|
|
100349
100655
|
return `${MCP_CATEGORY_PREFIX}${address}`;
|
|
100350
100656
|
}
|
|
100351
|
-
return
|
|
100657
|
+
return lookupOwn({
|
|
100658
|
+
record: ZED_TO_CANONICAL_TOOL_NAMES,
|
|
100659
|
+
key: zedName
|
|
100660
|
+
}) ?? zedName;
|
|
100352
100661
|
}
|
|
100353
100662
|
/**
|
|
100354
100663
|
* Zed matches `always_allow`/`always_deny`/`always_confirm` regexes against the
|
|
@@ -100660,7 +100969,10 @@ var ZedPermissions = class ZedPermissions extends ToolPermissions {
|
|
|
100660
100969
|
const globalDefault = parsed.success ? parsed.data.default : void 0;
|
|
100661
100970
|
const permission = {};
|
|
100662
100971
|
const ensure = (category) => {
|
|
100663
|
-
const existing =
|
|
100972
|
+
const existing = lookupOwn({
|
|
100973
|
+
record: permission,
|
|
100974
|
+
key: category
|
|
100975
|
+
});
|
|
100664
100976
|
if (existing) return existing;
|
|
100665
100977
|
const created = {};
|
|
100666
100978
|
permission[category] = created;
|
|
@@ -101287,7 +101599,7 @@ function resolveToolOutputRoot({ outputRoot, toolTarget, global }) {
|
|
|
101287
101599
|
async function convertFromTool(params) {
|
|
101288
101600
|
resetRunWarningState();
|
|
101289
101601
|
const packagingTarget = [params.fromTool, ...params.toTools].find(isPackagingToolTarget);
|
|
101290
|
-
if (packagingTarget) throw new Error(`Plugin packaging target '${packagingTarget}' is not supported by convert. Use import and generate with
|
|
101602
|
+
if (packagingTarget) throw new Error(`Plugin packaging target '${packagingTarget}' is not supported by convert. Use import with the plugin directory as outputRoot (--output-root) and generate with it in outputRoots (--output-roots).`);
|
|
101291
101603
|
const ctx = params;
|
|
101292
101604
|
const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount, checksCount] = [
|
|
101293
101605
|
await runFeatureConvert(ctx, buildRulesStrategy(ctx)),
|
|
@@ -104535,12 +104847,6 @@ Object.defineProperty(exports, "isFileSystemError", {
|
|
|
104535
104847
|
return isFileSystemError;
|
|
104536
104848
|
}
|
|
104537
104849
|
});
|
|
104538
|
-
Object.defineProperty(exports, "isPackagingToolTarget", {
|
|
104539
|
-
enumerable: true,
|
|
104540
|
-
get: function() {
|
|
104541
|
-
return isPackagingToolTarget;
|
|
104542
|
-
}
|
|
104543
|
-
});
|
|
104544
104850
|
Object.defineProperty(exports, "isRecord", {
|
|
104545
104851
|
enumerable: true,
|
|
104546
104852
|
get: function() {
|
|
@@ -104607,6 +104913,12 @@ Object.defineProperty(exports, "parseCommaSeparatedList", {
|
|
|
104607
104913
|
return parseCommaSeparatedList;
|
|
104608
104914
|
}
|
|
104609
104915
|
});
|
|
104916
|
+
Object.defineProperty(exports, "parseConfigFileContent", {
|
|
104917
|
+
enumerable: true,
|
|
104918
|
+
get: function() {
|
|
104919
|
+
return parseConfigFileContent;
|
|
104920
|
+
}
|
|
104921
|
+
});
|
|
104610
104922
|
Object.defineProperty(exports, "parseJsonc", {
|
|
104611
104923
|
enumerable: true,
|
|
104612
104924
|
get: function() {
|