@gotgenes/pi-permission-system 1.2.1 → 3.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/CHANGELOG.md +44 -0
- package/LICENSE +1 -1
- package/README.md +93 -36
- package/config/config.example.json +6 -0
- package/package.json +1 -1
- package/schemas/permissions.schema.json +18 -4
- package/src/config-loader.ts +398 -0
- package/src/config-paths.ts +34 -0
- package/src/config-reporter.ts +16 -8
- package/src/index.ts +98 -112
- package/src/permission-manager.ts +25 -111
- package/tests/config-loader.test.ts +364 -0
- package/tests/config-paths.test.ts +78 -0
- package/tests/config-reporter.test.ts +42 -33
- package/tests/extension-config.test.ts +51 -0
- package/tests/permission-system.test.ts +9 -26
- package/tests/session-start.test.ts +8 -33
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { normalize } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { isPermissionState, toRecord } from "./common.js";
|
|
5
|
+
import {
|
|
6
|
+
getGlobalConfigPath,
|
|
7
|
+
getLegacyExtensionConfigPath,
|
|
8
|
+
getLegacyGlobalPolicyPath,
|
|
9
|
+
getLegacyProjectPolicyPath,
|
|
10
|
+
getProjectConfigPath,
|
|
11
|
+
} from "./config-paths.js";
|
|
12
|
+
import type { PermissionDefaultPolicy, PermissionState } from "./types.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Unified config shape combining runtime knobs and policy in one object.
|
|
16
|
+
* All fields are optional so partial configs (project-only, global-only) work.
|
|
17
|
+
*/
|
|
18
|
+
export interface UnifiedPermissionConfig {
|
|
19
|
+
// Runtime knobs
|
|
20
|
+
debugLog?: boolean;
|
|
21
|
+
permissionReviewLog?: boolean;
|
|
22
|
+
yoloMode?: boolean;
|
|
23
|
+
|
|
24
|
+
// Policy
|
|
25
|
+
defaultPolicy?: Partial<PermissionDefaultPolicy>;
|
|
26
|
+
tools?: Record<string, PermissionState>;
|
|
27
|
+
bash?: Record<string, PermissionState>;
|
|
28
|
+
mcp?: Record<string, PermissionState>;
|
|
29
|
+
skills?: Record<string, PermissionState>;
|
|
30
|
+
special?: Record<string, PermissionState>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UnifiedConfigLoadResult {
|
|
34
|
+
config: UnifiedPermissionConfig;
|
|
35
|
+
issues: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const DEPRECATED_SPECIAL_KEYS: ReadonlySet<string> = new Set([
|
|
39
|
+
"tool_call_limit",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const BUILT_IN_TOOL_PERMISSION_NAMES = new Set([
|
|
43
|
+
"bash",
|
|
44
|
+
"read",
|
|
45
|
+
"write",
|
|
46
|
+
"edit",
|
|
47
|
+
"grep",
|
|
48
|
+
"find",
|
|
49
|
+
"ls",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const SPECIAL_PERMISSION_KEYS = new Set(["doom_loop", "external_directory"]);
|
|
53
|
+
|
|
54
|
+
export function stripJsonComments(input: string): string {
|
|
55
|
+
let output = "";
|
|
56
|
+
let inString = false;
|
|
57
|
+
let stringQuote: '"' | "'" | "" = "";
|
|
58
|
+
let escaping = false;
|
|
59
|
+
let inLineComment = false;
|
|
60
|
+
let inBlockComment = false;
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < input.length; i++) {
|
|
63
|
+
const char = input[i];
|
|
64
|
+
const next = input[i + 1] || "";
|
|
65
|
+
|
|
66
|
+
if (inLineComment) {
|
|
67
|
+
if (char === "\n") {
|
|
68
|
+
inLineComment = false;
|
|
69
|
+
output += char;
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (inBlockComment) {
|
|
75
|
+
if (char === "*" && next === "/") {
|
|
76
|
+
inBlockComment = false;
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!inString && char === "/" && next === "/") {
|
|
83
|
+
inLineComment = true;
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!inString && char === "/" && next === "*") {
|
|
89
|
+
inBlockComment = true;
|
|
90
|
+
i++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
output += char;
|
|
95
|
+
|
|
96
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
97
|
+
inString = true;
|
|
98
|
+
stringQuote = char;
|
|
99
|
+
escaping = false;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!inString) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (escaping) {
|
|
108
|
+
escaping = false;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (char === "\\") {
|
|
113
|
+
escaping = true;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (char === stringQuote) {
|
|
118
|
+
inString = false;
|
|
119
|
+
stringQuote = "";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return output;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizePartialPolicy(
|
|
127
|
+
value: unknown,
|
|
128
|
+
): Partial<PermissionDefaultPolicy> | undefined {
|
|
129
|
+
const record = toRecord(value);
|
|
130
|
+
const normalized: Partial<PermissionDefaultPolicy> = {};
|
|
131
|
+
let hasAny = false;
|
|
132
|
+
|
|
133
|
+
for (const key of ["tools", "bash", "mcp", "skills", "special"] as const) {
|
|
134
|
+
if (isPermissionState(record[key])) {
|
|
135
|
+
normalized[key] = record[key] as PermissionState;
|
|
136
|
+
hasAny = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return hasAny ? normalized : undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizePermissionRecord(
|
|
144
|
+
value: unknown,
|
|
145
|
+
): Record<string, PermissionState> | undefined {
|
|
146
|
+
const record = toRecord(value);
|
|
147
|
+
const normalized: Record<string, PermissionState> = {};
|
|
148
|
+
let hasAny = false;
|
|
149
|
+
|
|
150
|
+
for (const [key, state] of Object.entries(record)) {
|
|
151
|
+
if (isPermissionState(state)) {
|
|
152
|
+
normalized[key] = state;
|
|
153
|
+
hasAny = true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return hasAny ? normalized : undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function normalizeOptionalBoolean(value: unknown): boolean | undefined {
|
|
161
|
+
if (typeof value === "boolean") {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Normalize raw parsed JSON into the unified config shape.
|
|
169
|
+
* Handles top-level shorthand keys (e.g. `bash: "allow"` at root)
|
|
170
|
+
* and deprecated special keys, collecting issues along the way.
|
|
171
|
+
*/
|
|
172
|
+
export function normalizeUnifiedConfig(raw: unknown): {
|
|
173
|
+
config: UnifiedPermissionConfig;
|
|
174
|
+
issues: string[];
|
|
175
|
+
} {
|
|
176
|
+
const record = toRecord(raw);
|
|
177
|
+
const issues: string[] = [];
|
|
178
|
+
|
|
179
|
+
const config: UnifiedPermissionConfig = {};
|
|
180
|
+
|
|
181
|
+
// Runtime knobs
|
|
182
|
+
const debugLog = normalizeOptionalBoolean(record.debugLog);
|
|
183
|
+
if (debugLog !== undefined) config.debugLog = debugLog;
|
|
184
|
+
|
|
185
|
+
const permissionReviewLog = normalizeOptionalBoolean(
|
|
186
|
+
record.permissionReviewLog,
|
|
187
|
+
);
|
|
188
|
+
if (permissionReviewLog !== undefined)
|
|
189
|
+
config.permissionReviewLog = permissionReviewLog;
|
|
190
|
+
|
|
191
|
+
const yoloMode = normalizeOptionalBoolean(record.yoloMode);
|
|
192
|
+
if (yoloMode !== undefined) config.yoloMode = yoloMode;
|
|
193
|
+
|
|
194
|
+
// Policy
|
|
195
|
+
const defaultPolicy = normalizePartialPolicy(record.defaultPolicy);
|
|
196
|
+
if (defaultPolicy) config.defaultPolicy = defaultPolicy;
|
|
197
|
+
|
|
198
|
+
const tools = normalizePermissionRecord(record.tools);
|
|
199
|
+
if (tools) config.tools = tools;
|
|
200
|
+
|
|
201
|
+
const bash = normalizePermissionRecord(record.bash);
|
|
202
|
+
if (bash) config.bash = bash;
|
|
203
|
+
|
|
204
|
+
const mcp = normalizePermissionRecord(record.mcp);
|
|
205
|
+
if (mcp) config.mcp = mcp;
|
|
206
|
+
|
|
207
|
+
const skills = normalizePermissionRecord(record.skills);
|
|
208
|
+
if (skills) config.skills = skills;
|
|
209
|
+
|
|
210
|
+
const special = normalizePermissionRecord(record.special);
|
|
211
|
+
if (special) config.special = special;
|
|
212
|
+
|
|
213
|
+
// Detect deprecated special keys
|
|
214
|
+
const rawSpecial = toRecord(record.special);
|
|
215
|
+
for (const key of DEPRECATED_SPECIAL_KEYS) {
|
|
216
|
+
if (key in rawSpecial) {
|
|
217
|
+
issues.push(
|
|
218
|
+
`special.${key} is deprecated and ignored — remove it from your config file.`,
|
|
219
|
+
);
|
|
220
|
+
if (config.special) {
|
|
221
|
+
delete config.special[key];
|
|
222
|
+
if (Object.keys(config.special).length === 0) {
|
|
223
|
+
delete config.special;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Handle top-level shorthand keys (e.g. `bash: "allow"` at root level)
|
|
230
|
+
for (const [key, value] of Object.entries(record)) {
|
|
231
|
+
if (!isPermissionState(value)) continue;
|
|
232
|
+
|
|
233
|
+
if (BUILT_IN_TOOL_PERMISSION_NAMES.has(key)) {
|
|
234
|
+
config.tools = { ...(config.tools || {}), [key]: value };
|
|
235
|
+
} else if (SPECIAL_PERMISSION_KEYS.has(key)) {
|
|
236
|
+
config.special = { ...(config.special || {}), [key]: value };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { config, issues };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Merge two unified configs. Object-shaped fields (defaultPolicy, tools, bash,
|
|
245
|
+
* mcp, skills, special) are shallow-merged (override wins per-key). Scalar
|
|
246
|
+
* fields (debugLog, permissionReviewLog, yoloMode) are replaced when present
|
|
247
|
+
* in the override.
|
|
248
|
+
*/
|
|
249
|
+
export function mergeUnifiedConfigs(
|
|
250
|
+
base: UnifiedPermissionConfig,
|
|
251
|
+
override: UnifiedPermissionConfig,
|
|
252
|
+
): UnifiedPermissionConfig {
|
|
253
|
+
const merged: UnifiedPermissionConfig = {};
|
|
254
|
+
|
|
255
|
+
// Scalars: override replaces base when defined
|
|
256
|
+
for (const key of ["debugLog", "permissionReviewLog", "yoloMode"] as const) {
|
|
257
|
+
const value = override[key] ?? base[key];
|
|
258
|
+
if (value !== undefined) {
|
|
259
|
+
merged[key] = value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Object fields: shallow spread merge
|
|
264
|
+
for (const key of [
|
|
265
|
+
"defaultPolicy",
|
|
266
|
+
"tools",
|
|
267
|
+
"bash",
|
|
268
|
+
"mcp",
|
|
269
|
+
"skills",
|
|
270
|
+
"special",
|
|
271
|
+
] as const) {
|
|
272
|
+
const baseVal = base[key];
|
|
273
|
+
const overrideVal = override[key];
|
|
274
|
+
if (baseVal || overrideVal) {
|
|
275
|
+
merged[key] = { ...(baseVal || {}), ...(overrideVal || {}) } as never;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return merged;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface MergedConfigResult {
|
|
283
|
+
global: UnifiedPermissionConfig;
|
|
284
|
+
project: UnifiedPermissionConfig;
|
|
285
|
+
merged: UnifiedPermissionConfig;
|
|
286
|
+
issues: string[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Load global and project configs from the new layout, detect legacy files,
|
|
291
|
+
* merge everything, and collect issues.
|
|
292
|
+
*
|
|
293
|
+
* Merge order:
|
|
294
|
+
* 1. Legacy global policy (if present) — lowest precedence
|
|
295
|
+
* 2. Legacy extension runtime config (if present and path differs from new global)
|
|
296
|
+
* 3. New global config
|
|
297
|
+
* 4. Legacy project policy (if present)
|
|
298
|
+
* 5. New project config — highest precedence
|
|
299
|
+
*/
|
|
300
|
+
export function loadAndMergeConfigs(
|
|
301
|
+
agentDir: string,
|
|
302
|
+
cwd: string,
|
|
303
|
+
extensionRoot: string,
|
|
304
|
+
): MergedConfigResult {
|
|
305
|
+
const allIssues: string[] = [];
|
|
306
|
+
|
|
307
|
+
const newGlobalPath = getGlobalConfigPath(agentDir);
|
|
308
|
+
const newProjectPath = getProjectConfigPath(cwd);
|
|
309
|
+
const legacyGlobalPolicyPath = getLegacyGlobalPolicyPath(agentDir);
|
|
310
|
+
const legacyProjectPolicyPath = getLegacyProjectPolicyPath(cwd);
|
|
311
|
+
const legacyExtConfigPath = getLegacyExtensionConfigPath(extensionRoot);
|
|
312
|
+
|
|
313
|
+
// Start with empty
|
|
314
|
+
let merged: UnifiedPermissionConfig = {};
|
|
315
|
+
|
|
316
|
+
// 1. Legacy global policy
|
|
317
|
+
if (existsSync(legacyGlobalPolicyPath)) {
|
|
318
|
+
const legacy = loadUnifiedConfig(legacyGlobalPolicyPath);
|
|
319
|
+
allIssues.push(
|
|
320
|
+
`Legacy global policy found at '${legacyGlobalPolicyPath}'. ` +
|
|
321
|
+
`Move it to '${newGlobalPath}':\n` +
|
|
322
|
+
` mv '${legacyGlobalPolicyPath}' '${newGlobalPath}'`,
|
|
323
|
+
);
|
|
324
|
+
allIssues.push(...legacy.issues);
|
|
325
|
+
merged = mergeUnifiedConfigs(merged, legacy.config);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 2. Legacy extension runtime config (only if different from new global path)
|
|
329
|
+
const normalizedLegacyExt = normalize(legacyExtConfigPath);
|
|
330
|
+
const normalizedNewGlobal = normalize(newGlobalPath);
|
|
331
|
+
if (
|
|
332
|
+
normalizedLegacyExt !== normalizedNewGlobal &&
|
|
333
|
+
existsSync(legacyExtConfigPath)
|
|
334
|
+
) {
|
|
335
|
+
const legacy = loadUnifiedConfig(legacyExtConfigPath);
|
|
336
|
+
allIssues.push(
|
|
337
|
+
`Legacy extension config found at '${legacyExtConfigPath}'. ` +
|
|
338
|
+
`Move runtime settings to '${newGlobalPath}':\n` +
|
|
339
|
+
` mv '${legacyExtConfigPath}' '${newGlobalPath}'`,
|
|
340
|
+
);
|
|
341
|
+
allIssues.push(...legacy.issues);
|
|
342
|
+
merged = mergeUnifiedConfigs(merged, legacy.config);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 3. New global config
|
|
346
|
+
const globalResult = loadUnifiedConfig(newGlobalPath);
|
|
347
|
+
allIssues.push(...globalResult.issues);
|
|
348
|
+
const globalConfig = globalResult.config;
|
|
349
|
+
merged = mergeUnifiedConfigs(merged, globalConfig);
|
|
350
|
+
|
|
351
|
+
// 4. Legacy project policy
|
|
352
|
+
if (existsSync(legacyProjectPolicyPath)) {
|
|
353
|
+
const legacy = loadUnifiedConfig(legacyProjectPolicyPath);
|
|
354
|
+
allIssues.push(
|
|
355
|
+
`Legacy project policy found at '${legacyProjectPolicyPath}'. ` +
|
|
356
|
+
`Move it to '${newProjectPath}':\n` +
|
|
357
|
+
` mv '${legacyProjectPolicyPath}' '${newProjectPath}'`,
|
|
358
|
+
);
|
|
359
|
+
allIssues.push(...legacy.issues);
|
|
360
|
+
merged = mergeUnifiedConfigs(merged, legacy.config);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 5. New project config
|
|
364
|
+
const projectResult = loadUnifiedConfig(newProjectPath);
|
|
365
|
+
allIssues.push(...projectResult.issues);
|
|
366
|
+
const projectConfig = projectResult.config;
|
|
367
|
+
merged = mergeUnifiedConfigs(merged, projectConfig);
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
global: globalConfig,
|
|
371
|
+
project: projectConfig,
|
|
372
|
+
merged,
|
|
373
|
+
issues: allIssues,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Load and normalize a unified config file.
|
|
379
|
+
* Returns an empty config with no issues if the file does not exist.
|
|
380
|
+
* Returns an empty config with an issue if the file cannot be parsed.
|
|
381
|
+
*/
|
|
382
|
+
export function loadUnifiedConfig(path: string): UnifiedConfigLoadResult {
|
|
383
|
+
if (!existsSync(path)) {
|
|
384
|
+
return { config: {}, issues: [] };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
const raw = readFileSync(path, "utf-8");
|
|
389
|
+
const parsed = JSON.parse(stripJsonComments(raw)) as unknown;
|
|
390
|
+
return normalizeUnifiedConfig(parsed);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
+
return {
|
|
394
|
+
config: {},
|
|
395
|
+
issues: [`Failed to read config at '${path}': ${message}`],
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
const EXTENSION_ID = "pi-permission-system";
|
|
4
|
+
|
|
5
|
+
export const DEBUG_LOG_FILENAME = `${EXTENSION_ID}-debug.jsonl`;
|
|
6
|
+
export const REVIEW_LOG_FILENAME = `${EXTENSION_ID}-permission-review.jsonl`;
|
|
7
|
+
|
|
8
|
+
export function getGlobalConfigDir(agentDir: string): string {
|
|
9
|
+
return join(agentDir, "extensions", EXTENSION_ID);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getGlobalConfigPath(agentDir: string): string {
|
|
13
|
+
return join(getGlobalConfigDir(agentDir), "config.json");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getGlobalLogsDir(agentDir: string): string {
|
|
17
|
+
return join(getGlobalConfigDir(agentDir), "logs");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getProjectConfigPath(cwd: string): string {
|
|
21
|
+
return join(cwd, ".pi", "extensions", EXTENSION_ID, "config.json");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getLegacyGlobalPolicyPath(agentDir: string): string {
|
|
25
|
+
return join(agentDir, "pi-permissions.jsonc");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getLegacyProjectPolicyPath(cwd: string): string {
|
|
29
|
+
return join(cwd, ".pi", "agent", "pi-permissions.jsonc");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getLegacyExtensionConfigPath(extensionRoot: string): string {
|
|
33
|
+
return join(extensionRoot, "config.json");
|
|
34
|
+
}
|
package/src/config-reporter.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { ResolvedPolicyPaths } from "./permission-manager.js";
|
|
2
2
|
|
|
3
3
|
export interface ResolvedConfigLogEntry {
|
|
4
|
-
extensionConfigPath: string;
|
|
5
|
-
extensionConfigExists: boolean;
|
|
6
4
|
globalConfigPath: string;
|
|
7
5
|
globalConfigExists: boolean;
|
|
8
6
|
projectConfigPath: string | null;
|
|
@@ -11,16 +9,26 @@ export interface ResolvedConfigLogEntry {
|
|
|
11
9
|
agentsDirExists: boolean;
|
|
12
10
|
projectAgentsDir: string | null;
|
|
13
11
|
projectAgentsDirExists: boolean;
|
|
12
|
+
legacyGlobalPolicyDetected: boolean;
|
|
13
|
+
legacyProjectPolicyDetected: boolean;
|
|
14
|
+
legacyExtensionConfigDetected: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface BuildResolvedConfigLogEntryOptions {
|
|
18
|
+
policyPaths: ResolvedPolicyPaths;
|
|
19
|
+
legacyGlobalPolicyDetected?: boolean;
|
|
20
|
+
legacyProjectPolicyDetected?: boolean;
|
|
21
|
+
legacyExtensionConfigDetected?: boolean;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
export function buildResolvedConfigLogEntry(
|
|
17
|
-
|
|
18
|
-
extensionConfigExists: boolean,
|
|
19
|
-
policyPaths: ResolvedPolicyPaths,
|
|
25
|
+
options: BuildResolvedConfigLogEntryOptions,
|
|
20
26
|
): ResolvedConfigLogEntry {
|
|
21
27
|
return {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
...options.policyPaths,
|
|
29
|
+
legacyGlobalPolicyDetected: options.legacyGlobalPolicyDetected ?? false,
|
|
30
|
+
legacyProjectPolicyDetected: options.legacyProjectPolicyDetected ?? false,
|
|
31
|
+
legacyExtensionConfigDetected:
|
|
32
|
+
options.legacyExtensionConfigDetected ?? false,
|
|
25
33
|
};
|
|
26
34
|
}
|