rulesync 18.0.0 → 20.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.cjs +4 -4
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-KY5Q7laH.cjs → import-Cp3q904n.cjs} +193 -40
- package/dist/{import-CA3RFpGB.js → import-_UbLpXUZ.js} +194 -41
- package/dist/import-_UbLpXUZ.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +10 -10
- package/dist/import-CA3RFpGB.js.map +0 -1
|
@@ -5254,8 +5254,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
5254
5254
|
/**
|
|
5255
5255
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
5256
5256
|
*
|
|
5257
|
-
* Hermes validates hook events against a fixed `VALID_HOOKS` set —
|
|
5258
|
-
* of v0.21.
|
|
5257
|
+
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 41 entries as
|
|
5258
|
+
* of v0.21.5 (`v2026.9.24`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
|
|
5259
5259
|
* full list. Only the events with a clean 1:1 canonical equivalent are mapped
|
|
5260
5260
|
* here. All other native events round-trip through `hermesagent.hooks`.
|
|
5261
5261
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
@@ -5279,13 +5279,14 @@ const HERMESAGENT_HOOK_EVENTS = [
|
|
|
5279
5279
|
* observers, `gateway_platform_event` and `transform_api_error_classification`.
|
|
5280
5280
|
* Shell hooks are gated on the same set.
|
|
5281
5281
|
*
|
|
5282
|
-
* Verified against the tag `v2026.9.
|
|
5282
|
+
* Verified against the tag `v2026.9.24` (v0.21.5): `VALID_HOOKS` holds 41
|
|
5283
5283
|
* entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
|
|
5284
|
-
* (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity
|
|
5285
|
-
*
|
|
5284
|
+
* (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`,
|
|
5285
|
+
* and v0.21.5 added `pre_auxiliary_call` and `post_auxiliary_call`.
|
|
5286
|
+
* This list holds 40 of them — see
|
|
5286
5287
|
* {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
|
|
5287
5288
|
*
|
|
5288
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.
|
|
5289
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py
|
|
5289
5290
|
*/
|
|
5290
5291
|
const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
5291
5292
|
"pre_tool_call",
|
|
@@ -5303,6 +5304,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5303
5304
|
"pre_api_request",
|
|
5304
5305
|
"post_api_request",
|
|
5305
5306
|
"api_request_error",
|
|
5307
|
+
"pre_auxiliary_call",
|
|
5308
|
+
"post_auxiliary_call",
|
|
5306
5309
|
"on_session_start",
|
|
5307
5310
|
"on_session_end",
|
|
5308
5311
|
"on_session_finalize",
|
|
@@ -5337,8 +5340,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5337
5340
|
* rulesync only ever writes shell hooks, so these are excluded from
|
|
5338
5341
|
* {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
|
|
5339
5342
|
*
|
|
5340
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.
|
|
5341
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.
|
|
5343
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
|
|
5344
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/agent/shell_hooks.py — `_parse_hooks_block`
|
|
5342
5345
|
*/
|
|
5343
5346
|
const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
|
|
5344
5347
|
/**
|
|
@@ -26053,7 +26056,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
26053
26056
|
for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
|
|
26054
26057
|
if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
|
|
26055
26058
|
if (HERMESAGENT_SHELL_UNSUPPORTED_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is Python-plugin-only; Hermes Agent refuses it for shell hooks, so the generated entry will never run.`);
|
|
26056
|
-
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.
|
|
26059
|
+
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.5; preserving it for forward compatibility.`);
|
|
26057
26060
|
setHermesHookEntries({
|
|
26058
26061
|
result,
|
|
26059
26062
|
event: nativeEvent,
|
|
@@ -44111,6 +44114,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
44111
44114
|
//#region src/features/permissions/codexcli-permissions.ts
|
|
44112
44115
|
const RULESYNC_PROFILE_NAME = "rulesync";
|
|
44113
44116
|
const CODEX_WORKSPACE_ROOTS_KEY = ":workspace_roots";
|
|
44117
|
+
const CODEX_WORKSPACE_ROOT_SUBPATH = ".";
|
|
44114
44118
|
const CODEX_WORKSPACE_BASELINE = ":workspace";
|
|
44115
44119
|
const CODEX_READ_ONLY_BASELINE = ":read-only";
|
|
44116
44120
|
const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
|
|
@@ -44328,10 +44332,19 @@ function convertRulesyncToCodexProfile({ config, logger }) {
|
|
|
44328
44332
|
applyDefaultGitWriteRules({
|
|
44329
44333
|
config,
|
|
44330
44334
|
filesystem,
|
|
44331
|
-
workspaceRootFilesystem
|
|
44335
|
+
workspaceRootFilesystem,
|
|
44336
|
+
logger
|
|
44332
44337
|
});
|
|
44333
44338
|
if (Object.keys(workspaceRootFilesystem).length > 0) {
|
|
44334
|
-
|
|
44339
|
+
const directWorkspaceRootsAccess = filesystem[CODEX_WORKSPACE_ROOTS_KEY];
|
|
44340
|
+
if (isCodexFilesystemAccess(directWorkspaceRootsAccess)) {
|
|
44341
|
+
mergeIntoWorkspaceRootTable({
|
|
44342
|
+
workspaceRootFilesystem,
|
|
44343
|
+
pattern: CODEX_WORKSPACE_ROOT_SUBPATH,
|
|
44344
|
+
access: directWorkspaceRootsAccess
|
|
44345
|
+
});
|
|
44346
|
+
logger?.warn(`"${CODEX_WORKSPACE_ROOTS_KEY}" is set as a direct filesystem access rule in the permissions alongside workspace-relative rules; it is emitted as the "${CODEX_WORKSPACE_ROOT_SUBPATH}" entry of the "${CODEX_WORKSPACE_ROOTS_KEY}" table.`);
|
|
44347
|
+
}
|
|
44335
44348
|
if (Object.keys(workspaceRootFilesystem).some((pattern) => pattern.includes("**"))) filesystem.glob_scan_max_depth = CODEX_GLOB_SCAN_MAX_DEPTH;
|
|
44336
44349
|
filesystem[CODEX_WORKSPACE_ROOTS_KEY] = workspaceRootFilesystem;
|
|
44337
44350
|
}
|
|
@@ -44348,9 +44361,13 @@ function convertRulesyncToCodexProfile({ config, logger }) {
|
|
|
44348
44361
|
};
|
|
44349
44362
|
}
|
|
44350
44363
|
function addCodexFilesystemRules({ categoryRules, filesystem, workspaceRootFilesystem, logger }) {
|
|
44351
|
-
|
|
44364
|
+
const normalizedCategoryRules = normalizeWorkspaceRelativeCategoryRules({
|
|
44352
44365
|
categoryRules,
|
|
44353
44366
|
logger
|
|
44367
|
+
});
|
|
44368
|
+
for (const rule of mergeFilesystemCategoryRules({
|
|
44369
|
+
categoryRules: normalizedCategoryRules,
|
|
44370
|
+
logger
|
|
44354
44371
|
})) {
|
|
44355
44372
|
const access = normalizeCodexFilesystemAccess({
|
|
44356
44373
|
pattern: rule.pattern,
|
|
@@ -44370,11 +44387,32 @@ function addCodexFilesystemRules({ categoryRules, filesystem, workspaceRootFiles
|
|
|
44370
44387
|
});
|
|
44371
44388
|
}
|
|
44372
44389
|
}
|
|
44373
|
-
function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem }) {
|
|
44390
|
+
function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem, logger }) {
|
|
44374
44391
|
if (config.codexcli?.git_write_rules === false) return;
|
|
44375
44392
|
if (config.codexcli?.base_permission_profile === CODEX_READ_ONLY_BASELINE) return;
|
|
44376
44393
|
if (typeof filesystem[CODEX_WORKSPACE_ROOTS_KEY] === "string") return;
|
|
44377
|
-
for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES))
|
|
44394
|
+
for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) {
|
|
44395
|
+
if (workspaceRootFilesystem[pattern] !== void 0) continue;
|
|
44396
|
+
if (isRestrictedInWorkspaceRootTable({
|
|
44397
|
+
pattern,
|
|
44398
|
+
table: workspaceRootFilesystem
|
|
44399
|
+
})) {
|
|
44400
|
+
logger?.warn(`Skipping the default Codex CLI "${pattern}" = "${access}" carve-out: ".git" is covered by a broader workspace-relative deny or read-only rule, which the carve-out would reopen. To keep it, author read and edit allows for "${pattern}"; to silence this warning, set codexcli.git_write_rules to false.`);
|
|
44401
|
+
continue;
|
|
44402
|
+
}
|
|
44403
|
+
workspaceRootFilesystem[pattern] = access;
|
|
44404
|
+
}
|
|
44405
|
+
}
|
|
44406
|
+
function isRestrictedInWorkspaceRootTable({ pattern, table }) {
|
|
44407
|
+
const readRules = {};
|
|
44408
|
+
for (const [tablePattern, access] of Object.entries(table)) {
|
|
44409
|
+
if (tablePattern === pattern) continue;
|
|
44410
|
+
readRules[tablePattern] = access === "write" ? "allow" : "deny";
|
|
44411
|
+
}
|
|
44412
|
+
return isCoveredByReadRestriction({
|
|
44413
|
+
pattern,
|
|
44414
|
+
readRules
|
|
44415
|
+
});
|
|
44378
44416
|
}
|
|
44379
44417
|
function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
|
|
44380
44418
|
const permission = {};
|
|
@@ -44388,7 +44426,10 @@ function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
|
|
|
44388
44426
|
continue;
|
|
44389
44427
|
}
|
|
44390
44428
|
if (isCodexFilesystemRuleTable(access)) for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
|
|
44391
|
-
if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess
|
|
44429
|
+
if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess && !isRestrictedInWorkspaceRootTable({
|
|
44430
|
+
pattern: nestedPattern,
|
|
44431
|
+
table: access
|
|
44432
|
+
})) continue;
|
|
44392
44433
|
addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
|
|
44393
44434
|
}
|
|
44394
44435
|
}
|
|
@@ -44517,9 +44558,59 @@ function addFilesystemRule({ filesystem, workspaceRootFilesystem, pattern, acces
|
|
|
44517
44558
|
filesystem[pattern] = access;
|
|
44518
44559
|
return;
|
|
44519
44560
|
}
|
|
44520
|
-
|
|
44561
|
+
mergeIntoWorkspaceRootTable({
|
|
44562
|
+
workspaceRootFilesystem,
|
|
44563
|
+
pattern,
|
|
44564
|
+
access
|
|
44565
|
+
});
|
|
44566
|
+
}
|
|
44567
|
+
const PERMISSION_ACTION_RESTRICTIVENESS = {
|
|
44568
|
+
deny: 2,
|
|
44569
|
+
ask: 1,
|
|
44570
|
+
allow: 0
|
|
44571
|
+
};
|
|
44572
|
+
function normalizeWorkspaceRelativeCategoryRules({ categoryRules, logger }) {
|
|
44573
|
+
const result = {};
|
|
44574
|
+
for (const category of [
|
|
44575
|
+
"read",
|
|
44576
|
+
"edit",
|
|
44577
|
+
"write"
|
|
44578
|
+
]) {
|
|
44579
|
+
const rules = categoryRules[category];
|
|
44580
|
+
if (rules === void 0) continue;
|
|
44581
|
+
const normalized = {};
|
|
44582
|
+
for (const [pattern, action] of Object.entries(rules)) {
|
|
44583
|
+
const key = pattern.trim() === "" || canBeCodexFilesystemRoot(pattern) ? pattern : toWorkspaceRootSubpath(pattern);
|
|
44584
|
+
if (key === void 0) {
|
|
44585
|
+
logger?.warn(`Skipping Codex CLI ${category} filesystem entry for workspace-relative pattern "${pattern}": Codex rejects a ":workspace_roots" subpath containing a ".." segment or an empty leading segment and would refuse the whole config, so the rule is dropped entirely — even a deny — and the path is NOT protected by the generated profile. Use a normalized relative path instead.`);
|
|
44586
|
+
continue;
|
|
44587
|
+
}
|
|
44588
|
+
const existing = normalized[key];
|
|
44589
|
+
normalized[key] = existing === void 0 || PERMISSION_ACTION_RESTRICTIVENESS[action] > PERMISSION_ACTION_RESTRICTIVENESS[existing] ? action : existing;
|
|
44590
|
+
}
|
|
44591
|
+
result[category] = normalized;
|
|
44592
|
+
}
|
|
44593
|
+
return result;
|
|
44594
|
+
}
|
|
44595
|
+
function toWorkspaceRootSubpath(pattern) {
|
|
44596
|
+
let subpath = pattern;
|
|
44597
|
+
while (subpath.startsWith("./")) subpath = subpath.slice(2);
|
|
44598
|
+
if (subpath === "" || subpath === "**" && subpath !== pattern) return CODEX_WORKSPACE_ROOT_SUBPATH;
|
|
44599
|
+
if (subpath.startsWith("/") || subpath.split(/[\\/]/).some((segment) => segment === "..")) return;
|
|
44600
|
+
return subpath;
|
|
44601
|
+
}
|
|
44602
|
+
function mergeIntoWorkspaceRootTable({ workspaceRootFilesystem, pattern, access }) {
|
|
44603
|
+
const existing = workspaceRootFilesystem[pattern];
|
|
44604
|
+
workspaceRootFilesystem[pattern] = existing === void 0 ? access : moreRestrictiveCodexAccess({
|
|
44605
|
+
a: existing,
|
|
44606
|
+
b: access
|
|
44607
|
+
});
|
|
44521
44608
|
}
|
|
44522
44609
|
function normalizeCodexFilesystemAccess({ pattern, access, writeRestriction, logger }) {
|
|
44610
|
+
if (isWindowsDevicePath(pattern) && !canBeCodexFilesystemRoot(pattern)) {
|
|
44611
|
+
logger?.warn(`Skipping Codex CLI ${access} filesystem entry for Windows device path "${pattern}": Codex cannot express it as a workspace-root entry (it is rejected on Windows and matches nothing elsewhere), so the rule is dropped entirely — even a deny — and the path is NOT protected by the generated profile. Use a plain drive path such as "C:\\..." instead.`);
|
|
44612
|
+
return;
|
|
44613
|
+
}
|
|
44523
44614
|
if ((access === "read" || access === "write") && isWindowsDevicePath(pattern)) {
|
|
44524
44615
|
if (writeRestriction !== void 0) {
|
|
44525
44616
|
logger?.warn(`Codex CLI does not support Windows device paths for read/write access: pattern "${pattern}" will be emitted as "deny" because its write-side ${writeRestriction} restriction cannot be represented as read-only access. Use a plain drive path such as "C:\\..." instead.`);
|
|
@@ -44560,7 +44651,9 @@ function addRulesyncFilesystemRule(permission, pattern, access) {
|
|
|
44560
44651
|
permission.read ??= {};
|
|
44561
44652
|
permission.read[pattern] = "allow";
|
|
44562
44653
|
} else {
|
|
44654
|
+
permission.read ??= {};
|
|
44563
44655
|
permission.edit ??= {};
|
|
44656
|
+
permission.read[pattern] = "allow";
|
|
44564
44657
|
permission.edit[pattern] = "allow";
|
|
44565
44658
|
}
|
|
44566
44659
|
}
|
|
@@ -44696,10 +44789,13 @@ function mapWriteAction(action) {
|
|
|
44696
44789
|
* - A write-side non-allow → `"read"` (readable but not writable — exactly
|
|
44697
44790
|
* what Codex's `"read"` level expresses). An `ask` action is approximated
|
|
44698
44791
|
* this way because Codex has no path-level write approval.
|
|
44699
|
-
* - A write-side-only
|
|
44700
|
-
*
|
|
44701
|
-
* `X/id_rsa`, or `:root`) → `"deny"
|
|
44702
|
-
* path, so emitting `"read"`
|
|
44792
|
+
* - A write-side-only rule (no `read` rule for the same pattern) whose path
|
|
44793
|
+
* is covered by a broader `read` deny/ask rule (e.g. `X/**` covering
|
|
44794
|
+
* `X/id_rsa`, or `:root`) → `"deny"`, for a write-side `allow` too (with a
|
|
44795
|
+
* warning). Codex resolves the most specific path, so emitting `"read"` or
|
|
44796
|
+
* `"write"` there would re-open reads the user denied. Only the most
|
|
44797
|
+
* specific covering read rule counts: a narrower `read: allow` between the
|
|
44798
|
+
* broad deny and the path keeps the path readable.
|
|
44703
44799
|
* - A `read` non-allow on the same pattern → `"deny"` regardless of the
|
|
44704
44800
|
* write side; a contradictory write-side `allow` (unreadable but writable
|
|
44705
44801
|
* is not expressible in Codex) is warned about.
|
|
@@ -44747,10 +44843,11 @@ function mergeFilesystemCategoryRules({ categoryRules, logger }) {
|
|
|
44747
44843
|
}
|
|
44748
44844
|
const writeRestriction = writeAction === "allow" ? void 0 : writeAction;
|
|
44749
44845
|
const readUnspecified = readAction === void 0;
|
|
44750
|
-
if (readUnspecified &&
|
|
44846
|
+
if (readUnspecified && isCoveredByReadRestriction({
|
|
44751
44847
|
pattern,
|
|
44752
44848
|
readRules
|
|
44753
44849
|
})) {
|
|
44850
|
+
if (writeRestriction === void 0) logger?.warn(`Codex CLI cannot express "writable but not readable": pattern "${pattern}" has a write-side allow but is covered by a broader read deny/ask rule. Emitting "deny".`);
|
|
44754
44851
|
merged.push({
|
|
44755
44852
|
pattern,
|
|
44756
44853
|
access: "deny"
|
|
@@ -44767,33 +44864,89 @@ function mergeFilesystemCategoryRules({ categoryRules, logger }) {
|
|
|
44767
44864
|
return merged;
|
|
44768
44865
|
}
|
|
44769
44866
|
function collapseWriteSideRules(categoryRules) {
|
|
44770
|
-
const writeSideRestrictiveness = {
|
|
44771
|
-
deny: 2,
|
|
44772
|
-
ask: 1,
|
|
44773
|
-
allow: 0
|
|
44774
|
-
};
|
|
44775
44867
|
const writeSideRules = {};
|
|
44776
44868
|
for (const category of ["edit", "write"]) for (const [pattern, action] of Object.entries(categoryRules[category] ?? {})) {
|
|
44777
44869
|
const existing = writeSideRules[pattern];
|
|
44778
|
-
if (existing === void 0 ||
|
|
44870
|
+
if (existing === void 0 || PERMISSION_ACTION_RESTRICTIVENESS[action] > PERMISSION_ACTION_RESTRICTIVENESS[existing]) writeSideRules[pattern] = action;
|
|
44779
44871
|
}
|
|
44780
44872
|
return writeSideRules;
|
|
44781
44873
|
}
|
|
44782
44874
|
function isCoveredByReadRestriction({ pattern, readRules }) {
|
|
44783
|
-
const key =
|
|
44784
|
-
|
|
44785
|
-
|
|
44786
|
-
|
|
44787
|
-
|
|
44788
|
-
|
|
44789
|
-
|
|
44790
|
-
|
|
44791
|
-
|
|
44792
|
-
|
|
44793
|
-
|
|
44875
|
+
const key = normalizeCoverageKey(pattern);
|
|
44876
|
+
const isWorkspaceRelative = !canBeCodexFilesystemRoot(pattern);
|
|
44877
|
+
const keyHasDotSegment = hasDotPathSegment(pattern);
|
|
44878
|
+
let best;
|
|
44879
|
+
for (const [readPattern, action] of Object.entries(readRules)) {
|
|
44880
|
+
if (readPattern === pattern) continue;
|
|
44881
|
+
const specificity = coverageSpecificity({
|
|
44882
|
+
readPattern,
|
|
44883
|
+
key,
|
|
44884
|
+
isWorkspaceRelative
|
|
44885
|
+
});
|
|
44886
|
+
if (specificity === void 0) continue;
|
|
44887
|
+
const restrictive = action !== "allow";
|
|
44888
|
+
if (!restrictive && isSkippedCodexGrant(readPattern)) continue;
|
|
44889
|
+
if (!restrictive && (keyHasDotSegment || hasDotPathSegment(readPattern))) continue;
|
|
44890
|
+
if (best === void 0 || specificity > best.specificity || specificity === best.specificity && restrictive) best = {
|
|
44891
|
+
specificity,
|
|
44892
|
+
restrictive
|
|
44893
|
+
};
|
|
44894
|
+
}
|
|
44895
|
+
return best?.restrictive ?? false;
|
|
44896
|
+
}
|
|
44897
|
+
function coverageSpecificity({ readPattern, key, isWorkspaceRelative }) {
|
|
44898
|
+
if (readPattern === ":root" || readPattern === "/**") return 0;
|
|
44899
|
+
if (readPattern === CODEX_WORKSPACE_ROOTS_KEY) return isWorkspaceRelative ? 1 : void 0;
|
|
44900
|
+
if (canBeCodexFilesystemRoot(readPattern) === isWorkspaceRelative) return;
|
|
44901
|
+
const normalized = normalizeCoverageKey(readPattern);
|
|
44902
|
+
if (normalized === "**" || normalized === ".") return isWorkspaceRelative ? 1 : void 0;
|
|
44903
|
+
const withoutTrailingGlob = normalized.endsWith("/**") ? normalized.slice(0, -3) : normalized;
|
|
44904
|
+
if (withoutTrailingGlob.startsWith(":") || hasCodexFilesystemGlob(withoutTrailingGlob)) return;
|
|
44905
|
+
const base = stripTrailingSlash(withoutTrailingGlob);
|
|
44906
|
+
if (base === "") return;
|
|
44907
|
+
return key === base || key.startsWith(base.endsWith("/") ? base : `${base}/`) ? 2 + base.length : void 0;
|
|
44908
|
+
}
|
|
44909
|
+
function normalizeCoverageKey(pattern) {
|
|
44910
|
+
const form = toCoverageForm(pattern);
|
|
44911
|
+
return canBeCodexFilesystemRoot(pattern) && !pattern.startsWith(":") ? resolveDotSegments(form) : form;
|
|
44912
|
+
}
|
|
44913
|
+
function resolveDotSegments(path) {
|
|
44914
|
+
const [anchor = "", ...rest] = path.split("/");
|
|
44915
|
+
const stack = [];
|
|
44916
|
+
for (const segment of rest) {
|
|
44917
|
+
if (segment === ".") continue;
|
|
44918
|
+
if (segment === "..") {
|
|
44919
|
+
if (stack.length > 0 && stack[stack.length - 1] !== "..") stack.pop();
|
|
44920
|
+
else if (anchor === "~") stack.push(segment);
|
|
44921
|
+
continue;
|
|
44922
|
+
}
|
|
44923
|
+
stack.push(segment);
|
|
44924
|
+
}
|
|
44925
|
+
const resolved = [anchor, ...stack].join("/");
|
|
44926
|
+
return resolved === "" ? "/" : stripTrailingSlash(resolved);
|
|
44927
|
+
}
|
|
44928
|
+
function toCoverageForm(pattern) {
|
|
44929
|
+
let normalized = /^[A-Za-z]:[\\/]/.test(pattern) ? `${pattern.charAt(0).toLowerCase()}${pattern.slice(1).replaceAll("\\", "/")}` : pattern;
|
|
44930
|
+
while (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
44931
|
+
normalized = stripTrailingSlash(normalized);
|
|
44932
|
+
return normalized === "" && pattern !== "" ? "." : normalized;
|
|
44933
|
+
}
|
|
44934
|
+
function hasDotPathSegment(pattern) {
|
|
44935
|
+
const form = toCoverageForm(pattern);
|
|
44936
|
+
if (form === ".") return false;
|
|
44937
|
+
return form.split(/[\\/]/).some((segment) => segment === "." || segment === "..");
|
|
44794
44938
|
}
|
|
44795
|
-
function
|
|
44796
|
-
return pattern
|
|
44939
|
+
function isSkippedCodexGrant(pattern) {
|
|
44940
|
+
return isWindowsDevicePath(pattern) || hasUnsupportedCodexFilesystemGlob(pattern);
|
|
44941
|
+
}
|
|
44942
|
+
function moreRestrictiveCodexAccess({ a, b }) {
|
|
44943
|
+
const rank = {
|
|
44944
|
+
deny: 0,
|
|
44945
|
+
none: 0,
|
|
44946
|
+
read: 1,
|
|
44947
|
+
write: 2
|
|
44948
|
+
};
|
|
44949
|
+
return rank[a] <= rank[b] ? a : b;
|
|
44797
44950
|
}
|
|
44798
44951
|
function stripTrailingSlash(pattern) {
|
|
44799
44952
|
return pattern.length > 1 && pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
|