rulesync 8.5.0 → 8.6.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/{chunk-Q5FDY7NL.js → chunk-ILMHM7BF.js} +479 -271
- package/dist/cli/index.cjs +444 -236
- package/dist/cli/index.js +2 -2
- package/dist/index.cjs +372 -162
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
stringifyFrontmatter,
|
|
75
75
|
toPosixPath,
|
|
76
76
|
writeFileContent
|
|
77
|
-
} from "../chunk-
|
|
77
|
+
} from "../chunk-ILMHM7BF.js";
|
|
78
78
|
|
|
79
79
|
// src/cli/index.ts
|
|
80
80
|
import { Command } from "commander";
|
|
@@ -4625,7 +4625,7 @@ function wrapCommand({
|
|
|
4625
4625
|
}
|
|
4626
4626
|
|
|
4627
4627
|
// src/cli/index.ts
|
|
4628
|
-
var getVersion = () => "8.
|
|
4628
|
+
var getVersion = () => "8.6.0";
|
|
4629
4629
|
function wrapCommand2(name, errorCode, handler) {
|
|
4630
4630
|
return wrapCommand({ name, errorCode, handler, getVersion });
|
|
4631
4631
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -9771,15 +9771,78 @@ function mapBashActionToDecision(action) {
|
|
|
9771
9771
|
|
|
9772
9772
|
// src/features/permissions/geminicli-permissions.ts
|
|
9773
9773
|
var import_node_path67 = require("path");
|
|
9774
|
+
var smolToml5 = __toESM(require("smol-toml"), 1);
|
|
9774
9775
|
var import_mini29 = require("zod/mini");
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9776
|
+
|
|
9777
|
+
// src/utils/logger.ts
|
|
9778
|
+
var BaseLogger = class {
|
|
9779
|
+
_verbose = false;
|
|
9780
|
+
_silent = false;
|
|
9781
|
+
constructor({ verbose = false, silent = false } = {}) {
|
|
9782
|
+
this._silent = silent;
|
|
9783
|
+
this._verbose = verbose && !silent;
|
|
9784
|
+
}
|
|
9785
|
+
get verbose() {
|
|
9786
|
+
return this._verbose;
|
|
9787
|
+
}
|
|
9788
|
+
get silent() {
|
|
9789
|
+
return this._silent;
|
|
9790
|
+
}
|
|
9791
|
+
configure({ verbose, silent }) {
|
|
9792
|
+
if (verbose && silent) {
|
|
9793
|
+
this._silent = false;
|
|
9794
|
+
if (!isEnvTest()) {
|
|
9795
|
+
this.onConflictingFlags();
|
|
9796
|
+
}
|
|
9797
|
+
}
|
|
9798
|
+
this._silent = silent;
|
|
9799
|
+
this._verbose = verbose && !silent;
|
|
9800
|
+
}
|
|
9801
|
+
onConflictingFlags() {
|
|
9802
|
+
console.warn("Both --verbose and --silent specified; --silent takes precedence");
|
|
9803
|
+
}
|
|
9804
|
+
};
|
|
9805
|
+
var ConsoleLogger = class extends BaseLogger {
|
|
9806
|
+
isSuppressed() {
|
|
9807
|
+
return isEnvTest() || this._silent;
|
|
9808
|
+
}
|
|
9809
|
+
get jsonMode() {
|
|
9810
|
+
return false;
|
|
9811
|
+
}
|
|
9812
|
+
captureData(_key, _value) {
|
|
9813
|
+
}
|
|
9814
|
+
getJsonData() {
|
|
9815
|
+
return {};
|
|
9816
|
+
}
|
|
9817
|
+
outputJson(_success, _error) {
|
|
9818
|
+
}
|
|
9819
|
+
info(message, ...args) {
|
|
9820
|
+
if (this.isSuppressed()) return;
|
|
9821
|
+
console.log(message, ...args);
|
|
9822
|
+
}
|
|
9823
|
+
success(message, ...args) {
|
|
9824
|
+
if (this.isSuppressed()) return;
|
|
9825
|
+
console.log(message, ...args);
|
|
9826
|
+
}
|
|
9827
|
+
warn(message, ...args) {
|
|
9828
|
+
if (this.isSuppressed()) return;
|
|
9829
|
+
console.warn(message, ...args);
|
|
9830
|
+
}
|
|
9831
|
+
// Errors are always emitted, even in silent mode
|
|
9832
|
+
error(message, _code, ...args) {
|
|
9833
|
+
if (isEnvTest()) return;
|
|
9834
|
+
const errorMessage = message instanceof Error ? message.message : message;
|
|
9835
|
+
console.error(errorMessage, ...args);
|
|
9836
|
+
}
|
|
9837
|
+
debug(message, ...args) {
|
|
9838
|
+
if (!this._verbose || this.isSuppressed()) return;
|
|
9839
|
+
console.log(message, ...args);
|
|
9840
|
+
}
|
|
9841
|
+
};
|
|
9842
|
+
|
|
9843
|
+
// src/features/permissions/geminicli-permissions.ts
|
|
9844
|
+
var GEMINICLI_POLICY_RELATIVE_DIR_PATH = (0, import_node_path67.join)(".gemini", "policies");
|
|
9845
|
+
var GEMINICLI_POLICY_FILE_NAME = "rulesync.toml";
|
|
9783
9846
|
var RULESYNC_TO_GEMINICLI_TOOL_NAME = {
|
|
9784
9847
|
bash: "run_shell_command",
|
|
9785
9848
|
read: "read_file",
|
|
@@ -9787,16 +9850,32 @@ var RULESYNC_TO_GEMINICLI_TOOL_NAME = {
|
|
|
9787
9850
|
write: "write_file",
|
|
9788
9851
|
webfetch: "web_fetch"
|
|
9789
9852
|
};
|
|
9853
|
+
var GEMINICLI_TO_RULESYNC_TOOL_NAME = Object.fromEntries(
|
|
9854
|
+
Object.entries(RULESYNC_TO_GEMINICLI_TOOL_NAME).map(([k, v]) => [v, k])
|
|
9855
|
+
);
|
|
9856
|
+
var PRIORITY_DENY = 1e6;
|
|
9857
|
+
var PRIORITY_ASK = 1e3;
|
|
9858
|
+
var PRIORITY_ALLOW = 1;
|
|
9859
|
+
var SINGLE_STAR_REGEX = '[^/\\"]*';
|
|
9860
|
+
var DOUBLE_STAR_REGEX = '[^\\"]*';
|
|
9861
|
+
var SINGLE_CHAR_REGEX = '[^/\\"]';
|
|
9862
|
+
var LEGACY_SINGLE_STAR_REGEX = '[^\\"]*';
|
|
9863
|
+
var LEGACY_DOUBLE_STAR_REGEX = ".*";
|
|
9864
|
+
var COMMAND_ARGS_ANCHOR = '"command":"';
|
|
9865
|
+
var VALUE_END_ANCHOR = '\\"';
|
|
9866
|
+
var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set([
|
|
9867
|
+
"__proto__",
|
|
9868
|
+
"constructor",
|
|
9869
|
+
"prototype"
|
|
9870
|
+
]);
|
|
9871
|
+
var moduleLogger = new ConsoleLogger();
|
|
9790
9872
|
var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
|
|
9791
9873
|
static getSettablePaths(_options = {}) {
|
|
9792
9874
|
return {
|
|
9793
|
-
relativeDirPath:
|
|
9794
|
-
relativeFilePath:
|
|
9875
|
+
relativeDirPath: GEMINICLI_POLICY_RELATIVE_DIR_PATH,
|
|
9876
|
+
relativeFilePath: GEMINICLI_POLICY_FILE_NAME
|
|
9795
9877
|
};
|
|
9796
9878
|
}
|
|
9797
|
-
isDeletable() {
|
|
9798
|
-
return false;
|
|
9799
|
-
}
|
|
9800
9879
|
static async fromFile({
|
|
9801
9880
|
baseDir = process.cwd(),
|
|
9802
9881
|
validate = true,
|
|
@@ -9804,7 +9883,7 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
|
|
|
9804
9883
|
}) {
|
|
9805
9884
|
const paths = this.getSettablePaths({ global });
|
|
9806
9885
|
const filePath = (0, import_node_path67.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
|
|
9807
|
-
const fileContent = await readFileContentOrNull(filePath) ??
|
|
9886
|
+
const fileContent = await readFileContentOrNull(filePath) ?? "";
|
|
9808
9887
|
return new _GeminicliPermissions({
|
|
9809
9888
|
baseDir,
|
|
9810
9889
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -9813,63 +9892,72 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
|
|
|
9813
9892
|
validate
|
|
9814
9893
|
});
|
|
9815
9894
|
}
|
|
9816
|
-
static
|
|
9895
|
+
static fromRulesyncPermissions({
|
|
9817
9896
|
baseDir = process.cwd(),
|
|
9818
9897
|
rulesyncPermissions,
|
|
9819
9898
|
validate = true,
|
|
9820
|
-
|
|
9821
|
-
|
|
9899
|
+
global = false,
|
|
9900
|
+
logger = moduleLogger
|
|
9822
9901
|
}) {
|
|
9823
9902
|
const paths = this.getSettablePaths({ global });
|
|
9824
|
-
const
|
|
9825
|
-
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
9826
|
-
const settingsResult = GeminiCliSettingsSchema.safeParse(JSON.parse(existingContent));
|
|
9827
|
-
if (!settingsResult.success) {
|
|
9828
|
-
throw new Error(
|
|
9829
|
-
`Failed to parse existing Gemini CLI settings at ${filePath}: ${formatError(settingsResult.error)}`
|
|
9830
|
-
);
|
|
9831
|
-
}
|
|
9832
|
-
const { allowed, exclude } = convertRulesyncToGeminicliTools({
|
|
9833
|
-
config: rulesyncPermissions.getJson(),
|
|
9834
|
-
logger
|
|
9835
|
-
});
|
|
9836
|
-
const merged = {
|
|
9837
|
-
...settingsResult.data,
|
|
9838
|
-
tools: {
|
|
9839
|
-
...settingsResult.data.tools,
|
|
9840
|
-
...allowed.length > 0 ? { allowed } : {},
|
|
9841
|
-
...exclude.length > 0 ? { exclude } : {}
|
|
9842
|
-
}
|
|
9843
|
-
};
|
|
9903
|
+
const fileContent = buildGeminicliPolicyContent(rulesyncPermissions.getJson(), logger);
|
|
9844
9904
|
return new _GeminicliPermissions({
|
|
9845
9905
|
baseDir,
|
|
9846
9906
|
relativeDirPath: paths.relativeDirPath,
|
|
9847
9907
|
relativeFilePath: paths.relativeFilePath,
|
|
9848
|
-
fileContent
|
|
9908
|
+
fileContent,
|
|
9849
9909
|
validate
|
|
9850
9910
|
});
|
|
9851
9911
|
}
|
|
9852
9912
|
toRulesyncPermissions() {
|
|
9853
|
-
let settings;
|
|
9854
|
-
try {
|
|
9855
|
-
const parsed = JSON.parse(this.getFileContent());
|
|
9856
|
-
settings = GeminiCliSettingsSchema.parse(parsed);
|
|
9857
|
-
} catch (error) {
|
|
9858
|
-
throw new Error(
|
|
9859
|
-
`Failed to parse Gemini CLI permissions content in ${(0, import_node_path67.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
|
|
9860
|
-
{ cause: error }
|
|
9861
|
-
);
|
|
9862
|
-
}
|
|
9863
9913
|
const permission = {};
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9914
|
+
const fileContent = this.getFileContent();
|
|
9915
|
+
if (fileContent.trim().length > 0) {
|
|
9916
|
+
let parsed;
|
|
9917
|
+
try {
|
|
9918
|
+
parsed = smolToml5.parse(fileContent);
|
|
9919
|
+
} catch (error) {
|
|
9920
|
+
throw new Error(
|
|
9921
|
+
`Failed to parse Gemini CLI policy TOML in ${(0, import_node_path67.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
|
|
9922
|
+
{ cause: error }
|
|
9923
|
+
);
|
|
9924
|
+
}
|
|
9925
|
+
const rules = extractRules(parsed, moduleLogger);
|
|
9926
|
+
for (const [index, rule] of rules.entries()) {
|
|
9927
|
+
const mappedCategory = Object.hasOwn(GEMINICLI_TO_RULESYNC_TOOL_NAME, rule.toolName) ? GEMINICLI_TO_RULESYNC_TOOL_NAME[rule.toolName] : void 0;
|
|
9928
|
+
const category = mappedCategory ?? rule.toolName;
|
|
9929
|
+
if (RESERVED_OBJECT_KEYS.has(category)) {
|
|
9930
|
+
moduleLogger.warn(
|
|
9931
|
+
`Skipping rule #${index} in ${this.getRelativeFilePath()}: toolName "${rule.toolName}" maps to a reserved object key ("${category}") and would risk prototype pollution.`
|
|
9932
|
+
);
|
|
9933
|
+
continue;
|
|
9934
|
+
}
|
|
9935
|
+
const action = mapFromGeminicliDecision(rule.decision);
|
|
9936
|
+
if (!action) {
|
|
9937
|
+
moduleLogger.warn(
|
|
9938
|
+
`Skipping rule #${index} (toolName="${rule.toolName}", commandPrefix=${JSON.stringify(rule.commandPrefix)}, argsPattern=${JSON.stringify(rule.argsPattern)}) in ${this.getRelativeFilePath()}: unknown decision ${JSON.stringify(rule.decision)}`
|
|
9939
|
+
);
|
|
9940
|
+
continue;
|
|
9941
|
+
}
|
|
9942
|
+
if (rule.toolName === "run_shell_command" && rule.commandPrefix !== void 0 && rule.argsPattern !== void 0) {
|
|
9943
|
+
moduleLogger.warn(
|
|
9944
|
+
`Rule #${index} in ${this.getRelativeFilePath()} sets both commandPrefix and argsPattern; rulesync will honor argsPattern and ignore commandPrefix=${JSON.stringify(rule.commandPrefix)}.`
|
|
9945
|
+
);
|
|
9946
|
+
}
|
|
9947
|
+
const pattern = extractPattern(rule);
|
|
9948
|
+
if (RESERVED_OBJECT_KEYS.has(pattern)) {
|
|
9949
|
+
moduleLogger.warn(
|
|
9950
|
+
`Skipping rule #${index} in ${this.getRelativeFilePath()}: pattern "${pattern}" is a reserved object key.`
|
|
9951
|
+
);
|
|
9952
|
+
continue;
|
|
9953
|
+
}
|
|
9954
|
+
const existing = Object.hasOwn(permission, category) ? permission[category] : void 0;
|
|
9955
|
+
const target = existing ?? {};
|
|
9956
|
+
if (existing === void 0) {
|
|
9957
|
+
permission[category] = target;
|
|
9958
|
+
}
|
|
9959
|
+
target[pattern] = action;
|
|
9960
|
+
}
|
|
9873
9961
|
}
|
|
9874
9962
|
return this.toRulesyncPermissionsDefault({
|
|
9875
9963
|
fileContent: JSON.stringify({ permission }, null, 2)
|
|
@@ -9887,50 +9975,238 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
|
|
|
9887
9975
|
baseDir,
|
|
9888
9976
|
relativeDirPath,
|
|
9889
9977
|
relativeFilePath,
|
|
9890
|
-
fileContent:
|
|
9978
|
+
fileContent: "",
|
|
9891
9979
|
validate: false
|
|
9892
9980
|
});
|
|
9893
9981
|
}
|
|
9894
9982
|
};
|
|
9895
|
-
function
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
const allowed = [];
|
|
9900
|
-
const exclude = [];
|
|
9901
|
-
for (const [toolName, rules] of Object.entries(config.permission)) {
|
|
9983
|
+
function buildGeminicliPolicyContent(config, logger) {
|
|
9984
|
+
const rules = [];
|
|
9985
|
+
let order = 0;
|
|
9986
|
+
for (const [toolName, entries] of Object.entries(config.permission)) {
|
|
9902
9987
|
const mappedToolName = RULESYNC_TO_GEMINICLI_TOOL_NAME[toolName] ?? toolName;
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
if (action === "ask") {
|
|
9908
|
-
logger?.warn(
|
|
9909
|
-
`Gemini CLI does not support explicit "ask" rules in settings. Skipping ${toolName}:${pattern}`
|
|
9988
|
+
for (const [pattern, action] of Object.entries(entries)) {
|
|
9989
|
+
if (pattern === "") {
|
|
9990
|
+
logger.warn(
|
|
9991
|
+
`Skipping rule "${toolName}: "": empty pattern is not a valid permission target and would silently match every invocation (bash) or nothing (other tools).`
|
|
9910
9992
|
);
|
|
9911
9993
|
continue;
|
|
9912
9994
|
}
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9995
|
+
if (hasUnsafeAnchorChar(pattern)) {
|
|
9996
|
+
logger.warn(
|
|
9997
|
+
`Skipping rule "${toolName}: ${pattern}": pattern contains a character (" or \\) that would break JSON-anchor matching in the Gemini CLI Policy Engine.`
|
|
9998
|
+
);
|
|
9999
|
+
continue;
|
|
10000
|
+
}
|
|
10001
|
+
const decision = mapToGeminicliDecision(action);
|
|
10002
|
+
if (mappedToolName === "run_shell_command" && (pattern === "*" || pattern === "**") && decision !== "ask_user") {
|
|
10003
|
+
logger.warn(
|
|
10004
|
+
`Skipping rule "${toolName}: ${pattern}" with decision ${decision}: bash match-all patterns are only supported with "ask" because they would otherwise affect every shell command.`
|
|
10005
|
+
);
|
|
10006
|
+
continue;
|
|
9918
10007
|
}
|
|
10008
|
+
const currentRule = {
|
|
10009
|
+
toolName: mappedToolName,
|
|
10010
|
+
decision,
|
|
10011
|
+
priority: priorityForDecision(decision)
|
|
10012
|
+
};
|
|
10013
|
+
if (mappedToolName === "run_shell_command") {
|
|
10014
|
+
applyShellPattern({ rule: currentRule, pattern, toolName, logger });
|
|
10015
|
+
} else if (pattern !== "*") {
|
|
10016
|
+
currentRule.argsPattern = buildNonShellArgsPattern(pattern);
|
|
10017
|
+
}
|
|
10018
|
+
rules.push({ rule: currentRule, order: order++ });
|
|
9919
10019
|
}
|
|
9920
10020
|
}
|
|
9921
|
-
|
|
10021
|
+
rules.sort((a, b) => {
|
|
10022
|
+
const diff = toNumber(b.rule.priority) - toNumber(a.rule.priority);
|
|
10023
|
+
return diff !== 0 ? diff : a.order - b.order;
|
|
10024
|
+
});
|
|
10025
|
+
return smolToml5.stringify({ rule: rules.map((entry) => entry.rule) });
|
|
9922
10026
|
}
|
|
9923
|
-
function
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
return
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
10027
|
+
function buildNonShellArgsPattern(pattern) {
|
|
10028
|
+
return `"${globPatternToRegex(pattern)}${VALUE_END_ANCHOR}`;
|
|
10029
|
+
}
|
|
10030
|
+
function hasUnsafeAnchorChar(pattern) {
|
|
10031
|
+
return pattern.includes('"') || pattern.includes("\\");
|
|
10032
|
+
}
|
|
10033
|
+
function toNumber(value) {
|
|
10034
|
+
return typeof value === "number" ? value : 0;
|
|
10035
|
+
}
|
|
10036
|
+
function applyShellPattern({
|
|
10037
|
+
rule,
|
|
10038
|
+
pattern,
|
|
10039
|
+
toolName,
|
|
10040
|
+
logger
|
|
10041
|
+
}) {
|
|
10042
|
+
if (pattern === "*") {
|
|
10043
|
+
return;
|
|
10044
|
+
}
|
|
10045
|
+
const trailingWildcardStripped = pattern.endsWith(" *") ? pattern.slice(0, -2) : pattern;
|
|
10046
|
+
if (hasGlobMetacharacter(trailingWildcardStripped)) {
|
|
10047
|
+
rule.argsPattern = `${COMMAND_ARGS_ANCHOR}${globPatternToRegex(pattern)}`;
|
|
10048
|
+
logger.warn(
|
|
10049
|
+
`Gemini CLI does not support glob metacharacters inside a bash command prefix; emitting argsPattern for rule "${toolName}: ${pattern}".`
|
|
10050
|
+
);
|
|
10051
|
+
return;
|
|
10052
|
+
}
|
|
10053
|
+
rule.commandPrefix = trailingWildcardStripped;
|
|
10054
|
+
}
|
|
10055
|
+
function hasGlobMetacharacter(pattern) {
|
|
10056
|
+
return /[*?[\]]/.test(pattern);
|
|
10057
|
+
}
|
|
10058
|
+
function priorityForDecision(decision) {
|
|
10059
|
+
if (decision === "deny") return PRIORITY_DENY;
|
|
10060
|
+
if (decision === "ask_user") return PRIORITY_ASK;
|
|
10061
|
+
return PRIORITY_ALLOW;
|
|
10062
|
+
}
|
|
10063
|
+
function mapToGeminicliDecision(action) {
|
|
10064
|
+
if (action === "ask") {
|
|
10065
|
+
return "ask_user";
|
|
10066
|
+
}
|
|
10067
|
+
return action;
|
|
10068
|
+
}
|
|
10069
|
+
function mapFromGeminicliDecision(decision) {
|
|
10070
|
+
if (decision === "allow") return "allow";
|
|
10071
|
+
if (decision === "deny") return "deny";
|
|
10072
|
+
if (decision === "ask_user") return "ask";
|
|
10073
|
+
return null;
|
|
10074
|
+
}
|
|
10075
|
+
function globPatternToRegex(pattern) {
|
|
10076
|
+
let regex = "";
|
|
10077
|
+
let i = 0;
|
|
10078
|
+
while (i < pattern.length) {
|
|
10079
|
+
const char = pattern[i];
|
|
10080
|
+
if (char === void 0) {
|
|
10081
|
+
break;
|
|
10082
|
+
}
|
|
10083
|
+
if (char === "*" && pattern[i + 1] === "*") {
|
|
10084
|
+
regex += DOUBLE_STAR_REGEX;
|
|
10085
|
+
i += 2;
|
|
10086
|
+
continue;
|
|
10087
|
+
}
|
|
10088
|
+
if (char === "*") {
|
|
10089
|
+
regex += SINGLE_STAR_REGEX;
|
|
10090
|
+
i += 1;
|
|
10091
|
+
continue;
|
|
10092
|
+
}
|
|
10093
|
+
if (char === "?") {
|
|
10094
|
+
regex += SINGLE_CHAR_REGEX;
|
|
10095
|
+
i += 1;
|
|
10096
|
+
continue;
|
|
10097
|
+
}
|
|
10098
|
+
if (char === "[") {
|
|
10099
|
+
regex += escapeRegexChar(char);
|
|
10100
|
+
i += 1;
|
|
10101
|
+
continue;
|
|
10102
|
+
}
|
|
10103
|
+
if (char === "]") {
|
|
10104
|
+
regex += escapeRegexChar(char);
|
|
10105
|
+
i += 1;
|
|
10106
|
+
continue;
|
|
10107
|
+
}
|
|
10108
|
+
if (isRegexMetacharacter(char)) {
|
|
10109
|
+
regex += `\\${char}`;
|
|
10110
|
+
i += 1;
|
|
10111
|
+
continue;
|
|
10112
|
+
}
|
|
10113
|
+
regex += char;
|
|
10114
|
+
i += 1;
|
|
10115
|
+
}
|
|
10116
|
+
return regex;
|
|
10117
|
+
}
|
|
10118
|
+
function escapeRegexChar(char) {
|
|
10119
|
+
return `\\${char}`;
|
|
10120
|
+
}
|
|
10121
|
+
function isRegexMetacharacter(char) {
|
|
10122
|
+
return /[.+^${}()|\\]/.test(char);
|
|
10123
|
+
}
|
|
10124
|
+
function regexToGlobPattern(regex) {
|
|
10125
|
+
let source = regex;
|
|
10126
|
+
if (source.endsWith(VALUE_END_ANCHOR)) {
|
|
10127
|
+
source = source.slice(0, -VALUE_END_ANCHOR.length);
|
|
10128
|
+
}
|
|
10129
|
+
let glob = "";
|
|
10130
|
+
let i = 0;
|
|
10131
|
+
while (i < source.length) {
|
|
10132
|
+
if (source.startsWith(DOUBLE_STAR_REGEX, i)) {
|
|
10133
|
+
glob += "**";
|
|
10134
|
+
i += DOUBLE_STAR_REGEX.length;
|
|
10135
|
+
continue;
|
|
10136
|
+
}
|
|
10137
|
+
if (source.startsWith(LEGACY_DOUBLE_STAR_REGEX, i)) {
|
|
10138
|
+
glob += "**";
|
|
10139
|
+
i += LEGACY_DOUBLE_STAR_REGEX.length;
|
|
10140
|
+
continue;
|
|
10141
|
+
}
|
|
10142
|
+
if (source.startsWith(SINGLE_STAR_REGEX, i)) {
|
|
10143
|
+
glob += "*";
|
|
10144
|
+
i += SINGLE_STAR_REGEX.length;
|
|
10145
|
+
continue;
|
|
10146
|
+
}
|
|
10147
|
+
if (source.startsWith(LEGACY_SINGLE_STAR_REGEX, i)) {
|
|
10148
|
+
glob += "*";
|
|
10149
|
+
i += LEGACY_SINGLE_STAR_REGEX.length;
|
|
10150
|
+
continue;
|
|
10151
|
+
}
|
|
10152
|
+
if (source.startsWith(SINGLE_CHAR_REGEX, i)) {
|
|
10153
|
+
glob += "?";
|
|
10154
|
+
i += SINGLE_CHAR_REGEX.length;
|
|
10155
|
+
continue;
|
|
10156
|
+
}
|
|
10157
|
+
const char = source[i];
|
|
10158
|
+
if (char === "\\") {
|
|
10159
|
+
const escaped = source[i + 1];
|
|
10160
|
+
if (escaped !== void 0) {
|
|
10161
|
+
glob += escaped;
|
|
10162
|
+
i += 2;
|
|
10163
|
+
continue;
|
|
10164
|
+
}
|
|
10165
|
+
}
|
|
10166
|
+
glob += char ?? "";
|
|
10167
|
+
i += 1;
|
|
10168
|
+
}
|
|
10169
|
+
return glob;
|
|
10170
|
+
}
|
|
10171
|
+
var GeminicliPolicyRuleSchema = import_mini29.z.looseObject({
|
|
10172
|
+
toolName: import_mini29.z.string(),
|
|
10173
|
+
decision: import_mini29.z.optional(import_mini29.z.unknown()),
|
|
10174
|
+
commandPrefix: import_mini29.z.optional(import_mini29.z.string()),
|
|
10175
|
+
argsPattern: import_mini29.z.optional(import_mini29.z.string())
|
|
10176
|
+
});
|
|
10177
|
+
var GeminicliPolicyFileSchema = import_mini29.z.looseObject({
|
|
10178
|
+
rule: import_mini29.z.optional(import_mini29.z.array(import_mini29.z.looseObject({})))
|
|
10179
|
+
});
|
|
10180
|
+
function extractRules(parsed, logger) {
|
|
10181
|
+
const parsedFile = GeminicliPolicyFileSchema.safeParse(parsed);
|
|
10182
|
+
if (!parsedFile.success || !parsedFile.data.rule) {
|
|
10183
|
+
return [];
|
|
10184
|
+
}
|
|
10185
|
+
const rules = [];
|
|
10186
|
+
for (const [index, entry] of parsedFile.data.rule.entries()) {
|
|
10187
|
+
const result = GeminicliPolicyRuleSchema.safeParse(entry);
|
|
10188
|
+
if (result.success) {
|
|
10189
|
+
rules.push(result.data);
|
|
10190
|
+
continue;
|
|
10191
|
+
}
|
|
10192
|
+
logger.warn(
|
|
10193
|
+
`Skipping malformed Gemini CLI policy rule at index ${index}: ${formatError(result.error)}`
|
|
10194
|
+
);
|
|
10195
|
+
}
|
|
10196
|
+
return rules;
|
|
10197
|
+
}
|
|
10198
|
+
function extractPattern(rule) {
|
|
10199
|
+
if (rule.toolName === "run_shell_command") {
|
|
10200
|
+
if (rule.argsPattern) {
|
|
10201
|
+
const stripped = rule.argsPattern.startsWith(COMMAND_ARGS_ANCHOR) ? rule.argsPattern.slice(COMMAND_ARGS_ANCHOR.length) : rule.argsPattern;
|
|
10202
|
+
return regexToGlobPattern(stripped);
|
|
10203
|
+
}
|
|
10204
|
+
if (!rule.commandPrefix) return "*";
|
|
10205
|
+
return rule.commandPrefix.endsWith(" *") || rule.commandPrefix.endsWith("*") ? rule.commandPrefix : `${rule.commandPrefix} *`;
|
|
10206
|
+
}
|
|
10207
|
+
if (!rule.argsPattern) return "*";
|
|
10208
|
+
const regex = rule.argsPattern.startsWith('"') ? rule.argsPattern.slice(1) : rule.argsPattern;
|
|
10209
|
+
return regexToGlobPattern(regex);
|
|
9934
10210
|
}
|
|
9935
10211
|
|
|
9936
10212
|
// src/features/permissions/kiro-permissions.ts
|
|
@@ -15188,7 +15464,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
|
|
|
15188
15464
|
|
|
15189
15465
|
// src/features/subagents/codexcli-subagent.ts
|
|
15190
15466
|
var import_node_path104 = require("path");
|
|
15191
|
-
var
|
|
15467
|
+
var smolToml6 = __toESM(require("smol-toml"), 1);
|
|
15192
15468
|
var import_mini58 = require("zod/mini");
|
|
15193
15469
|
var CodexCliSubagentTomlSchema = import_mini58.z.looseObject({
|
|
15194
15470
|
name: import_mini58.z.string(),
|
|
@@ -15200,13 +15476,13 @@ var CodexCliSubagentTomlSchema = import_mini58.z.looseObject({
|
|
|
15200
15476
|
});
|
|
15201
15477
|
function stringifyCodexCliSubagentToml(tomlObj) {
|
|
15202
15478
|
const { developer_instructions, ...restFields } = tomlObj;
|
|
15203
|
-
const restToml =
|
|
15479
|
+
const restToml = smolToml6.stringify(restFields).trimEnd();
|
|
15204
15480
|
if (developer_instructions === void 0) {
|
|
15205
15481
|
return restToml;
|
|
15206
15482
|
}
|
|
15207
|
-
const developerInstructionsToml = developer_instructions.includes("\n") ? developer_instructions.includes("'''") ?
|
|
15483
|
+
const developerInstructionsToml = developer_instructions.includes("\n") ? developer_instructions.includes("'''") ? smolToml6.stringify({ developer_instructions }).trimEnd() : `developer_instructions = '''
|
|
15208
15484
|
${developer_instructions}
|
|
15209
|
-
'''` :
|
|
15485
|
+
'''` : smolToml6.stringify({ developer_instructions }).trimEnd();
|
|
15210
15486
|
return [restToml, developerInstructionsToml].filter((value) => value.length > 0).join("\n");
|
|
15211
15487
|
}
|
|
15212
15488
|
var CodexCliSubagent = class _CodexCliSubagent extends ToolSubagent {
|
|
@@ -15214,7 +15490,7 @@ var CodexCliSubagent = class _CodexCliSubagent extends ToolSubagent {
|
|
|
15214
15490
|
constructor({ body, ...rest }) {
|
|
15215
15491
|
if (rest.validate !== false) {
|
|
15216
15492
|
try {
|
|
15217
|
-
const parsed =
|
|
15493
|
+
const parsed = smolToml6.parse(body);
|
|
15218
15494
|
CodexCliSubagentTomlSchema.parse(parsed);
|
|
15219
15495
|
} catch (error) {
|
|
15220
15496
|
throw new Error(
|
|
@@ -15239,7 +15515,7 @@ var CodexCliSubagent = class _CodexCliSubagent extends ToolSubagent {
|
|
|
15239
15515
|
toRulesyncSubagent() {
|
|
15240
15516
|
let parsed;
|
|
15241
15517
|
try {
|
|
15242
|
-
parsed = CodexCliSubagentTomlSchema.parse(
|
|
15518
|
+
parsed = CodexCliSubagentTomlSchema.parse(smolToml6.parse(this.body));
|
|
15243
15519
|
} catch (error) {
|
|
15244
15520
|
throw new Error(
|
|
15245
15521
|
`Failed to parse TOML in ${(0, import_node_path104.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -15300,7 +15576,7 @@ var CodexCliSubagent = class _CodexCliSubagent extends ToolSubagent {
|
|
|
15300
15576
|
}
|
|
15301
15577
|
validate() {
|
|
15302
15578
|
try {
|
|
15303
|
-
const parsed =
|
|
15579
|
+
const parsed = smolToml6.parse(this.body);
|
|
15304
15580
|
CodexCliSubagentTomlSchema.parse(parsed);
|
|
15305
15581
|
return { success: true, error: null };
|
|
15306
15582
|
} catch (error) {
|
|
@@ -21624,72 +21900,6 @@ async function importPermissionsCore(params) {
|
|
|
21624
21900
|
return writtenCount;
|
|
21625
21901
|
}
|
|
21626
21902
|
|
|
21627
|
-
// src/utils/logger.ts
|
|
21628
|
-
var BaseLogger = class {
|
|
21629
|
-
_verbose = false;
|
|
21630
|
-
_silent = false;
|
|
21631
|
-
constructor({ verbose = false, silent = false } = {}) {
|
|
21632
|
-
this._silent = silent;
|
|
21633
|
-
this._verbose = verbose && !silent;
|
|
21634
|
-
}
|
|
21635
|
-
get verbose() {
|
|
21636
|
-
return this._verbose;
|
|
21637
|
-
}
|
|
21638
|
-
get silent() {
|
|
21639
|
-
return this._silent;
|
|
21640
|
-
}
|
|
21641
|
-
configure({ verbose, silent }) {
|
|
21642
|
-
if (verbose && silent) {
|
|
21643
|
-
this._silent = false;
|
|
21644
|
-
if (!isEnvTest()) {
|
|
21645
|
-
this.onConflictingFlags();
|
|
21646
|
-
}
|
|
21647
|
-
}
|
|
21648
|
-
this._silent = silent;
|
|
21649
|
-
this._verbose = verbose && !silent;
|
|
21650
|
-
}
|
|
21651
|
-
onConflictingFlags() {
|
|
21652
|
-
console.warn("Both --verbose and --silent specified; --silent takes precedence");
|
|
21653
|
-
}
|
|
21654
|
-
};
|
|
21655
|
-
var ConsoleLogger = class extends BaseLogger {
|
|
21656
|
-
isSuppressed() {
|
|
21657
|
-
return isEnvTest() || this._silent;
|
|
21658
|
-
}
|
|
21659
|
-
get jsonMode() {
|
|
21660
|
-
return false;
|
|
21661
|
-
}
|
|
21662
|
-
captureData(_key, _value) {
|
|
21663
|
-
}
|
|
21664
|
-
getJsonData() {
|
|
21665
|
-
return {};
|
|
21666
|
-
}
|
|
21667
|
-
outputJson(_success, _error) {
|
|
21668
|
-
}
|
|
21669
|
-
info(message, ...args) {
|
|
21670
|
-
if (this.isSuppressed()) return;
|
|
21671
|
-
console.log(message, ...args);
|
|
21672
|
-
}
|
|
21673
|
-
success(message, ...args) {
|
|
21674
|
-
if (this.isSuppressed()) return;
|
|
21675
|
-
console.log(message, ...args);
|
|
21676
|
-
}
|
|
21677
|
-
warn(message, ...args) {
|
|
21678
|
-
if (this.isSuppressed()) return;
|
|
21679
|
-
console.warn(message, ...args);
|
|
21680
|
-
}
|
|
21681
|
-
// Errors are always emitted, even in silent mode
|
|
21682
|
-
error(message, _code, ...args) {
|
|
21683
|
-
if (isEnvTest()) return;
|
|
21684
|
-
const errorMessage = message instanceof Error ? message.message : message;
|
|
21685
|
-
console.error(errorMessage, ...args);
|
|
21686
|
-
}
|
|
21687
|
-
debug(message, ...args) {
|
|
21688
|
-
if (!this._verbose || this.isSuppressed()) return;
|
|
21689
|
-
console.log(message, ...args);
|
|
21690
|
-
}
|
|
21691
|
-
};
|
|
21692
|
-
|
|
21693
21903
|
// src/index.ts
|
|
21694
21904
|
async function generate2(options = {}) {
|
|
21695
21905
|
const { silent = true, verbose = false, ...rest } = options;
|
package/dist/index.js
CHANGED