dsh-perm-gate 2.1.0 → 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.
Files changed (77) hide show
  1. package/CHANGELOG.ja.md +82 -0
  2. package/CHANGELOG.ko.md +80 -0
  3. package/CHANGELOG.md +321 -0
  4. package/INSTALL.ja.md +70 -8
  5. package/INSTALL.ko.md +68 -9
  6. package/INSTALL.md +68 -8
  7. package/INSTALL.zh.md +59 -7
  8. package/README.ja.md +103 -16
  9. package/README.ko.md +101 -17
  10. package/README.md +152 -25
  11. package/README.zh.md +99 -12
  12. package/cordis.patch.yml +34 -3
  13. package/docs/baseline-guard.md +56 -0
  14. package/docs/plan-rules-to-settings.md +217 -0
  15. package/docs/rules-format.en.md +294 -0
  16. package/docs/rules-format.md +279 -0
  17. package/docs/ui-gap-analysis-and-branch-permissions.md +338 -0
  18. package/dsh.plugin.json +22 -0
  19. package/examples/permissions.example.yaml +16 -1
  20. package/lib/agent-identity.d.ts +43 -0
  21. package/lib/agent-identity.js +95 -0
  22. package/lib/allowlist.d.ts +27 -0
  23. package/lib/allowlist.js +103 -1
  24. package/lib/cli.js +11 -14
  25. package/lib/client.js +598 -6
  26. package/lib/client.js.map +1 -1
  27. package/lib/command-dispatcher.d.ts +22 -0
  28. package/lib/command-dispatcher.js +97 -0
  29. package/lib/command-semantics.d.ts +62 -0
  30. package/lib/command-semantics.js +6 -0
  31. package/lib/compiler.d.ts +50 -0
  32. package/lib/compiler.js +122 -0
  33. package/lib/config.d.ts +161 -7
  34. package/lib/config.js +78 -11
  35. package/lib/dry-run.d.ts +87 -0
  36. package/lib/dry-run.js +110 -0
  37. package/lib/engine.js +61 -4
  38. package/lib/evaluate.d.ts +9 -0
  39. package/lib/evaluate.js +264 -1
  40. package/lib/events.d.ts +69 -2
  41. package/lib/events.js +123 -0
  42. package/lib/index.d.ts +1 -1
  43. package/lib/index.js +320 -11
  44. package/lib/network-lifecycle.d.ts +107 -0
  45. package/lib/network-lifecycle.js +180 -0
  46. package/lib/network.d.ts +109 -0
  47. package/lib/network.js +196 -0
  48. package/lib/parsers/git.d.ts +43 -0
  49. package/lib/parsers/git.js +404 -0
  50. package/lib/parsers/shell-cmds.d.ts +32 -0
  51. package/lib/parsers/shell-cmds.js +293 -0
  52. package/lib/preset.d.ts +13 -0
  53. package/lib/preset.js +17 -0
  54. package/lib/proxy.d.ts +160 -0
  55. package/lib/proxy.js +529 -0
  56. package/lib/risk.d.ts +17 -3
  57. package/lib/risk.js +19 -6
  58. package/lib/rule-chain.d.ts +39 -0
  59. package/lib/rule-chain.js +213 -0
  60. package/lib/rule-dims.d.ts +174 -0
  61. package/lib/rule-dims.js +230 -0
  62. package/lib/rule.d.ts +60 -0
  63. package/lib/rule.js +69 -7
  64. package/lib/rules-view.d.ts +52 -0
  65. package/lib/rules-view.js +126 -0
  66. package/lib/runtime.d.ts +208 -7
  67. package/lib/runtime.js +433 -27
  68. package/lib/session-sweep.d.ts +45 -0
  69. package/lib/session-sweep.js +153 -0
  70. package/lib/shadow.d.ts +34 -0
  71. package/lib/shadow.js +143 -0
  72. package/lib/shell.d.ts +14 -0
  73. package/lib/shell.js +17 -0
  74. package/lib/watch.d.ts +42 -0
  75. package/lib/watch.js +170 -0
  76. package/package.json +15 -2
  77. package/scripts/patch-permission-glyph.mjs +209 -0
package/lib/rule.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type CompiledPattern } from './compiler.js';
2
+ import { type ParamsDimension, type AbsentDimension, type AgentsDimension, type WhenDimension, type ArgvDimension, type NetworkDimension, type BranchDimension } from './rule-dims.js';
2
3
  export type RuleAction = 'allow' | 'ask' | 'deny';
3
4
  /** A parsed, shape-validated permissions document (patterns not yet compiled). */
4
5
  export interface PermissionsDoc {
@@ -20,6 +21,20 @@ export interface RuleEntryDoc {
20
21
  readonly args: string[];
21
22
  /** Workspace-relative path globs; empty = no constraint. */
22
23
  readonly paths: string[];
24
+ /** Parameter key→value matching (AND over keys, `!` prefix negates). */
25
+ readonly params: ParamsDimension;
26
+ /** Parameter keys that must NOT be present. */
27
+ readonly absent: AbsentDimension;
28
+ /** Agent identity candidates (main / subagent / preset:<name>). */
29
+ readonly agents: AgentsDimension;
30
+ /** Environment / platform conditions. */
31
+ readonly when: WhenDimension | undefined;
32
+ /** Extra argv patterns (pipeline etc.). */
33
+ readonly argv: ArgvDimension | undefined;
34
+ /** Network dimension (domain / IP / port / scheme). */
35
+ readonly network: NetworkDimension | undefined;
36
+ /** Git branch / remote / protected-branch dimension. */
37
+ readonly branch: BranchDimension | undefined;
23
38
  readonly action: RuleAction;
24
39
  readonly reason: string;
25
40
  readonly enabled: boolean;
@@ -34,8 +49,28 @@ export interface CompiledRuleEntry {
34
49
  readonly command: readonly CommandSpec[];
35
50
  readonly args: readonly CompiledPattern[];
36
51
  readonly paths: readonly CompiledPattern[];
52
+ /** Parsed params dimension (raw patterns; compiled on match). */
53
+ readonly params: ParamsDimension;
54
+ /** Parsed absent dimension (raw key names). */
55
+ readonly absent: AbsentDimension;
56
+ /** Parsed agents dimension (raw patterns). */
57
+ readonly agents: AgentsDimension;
58
+ /** Parsed when dimension (raw conditions). */
59
+ readonly when: WhenDimension | undefined;
60
+ /** Parsed argv dimension (raw patterns). */
61
+ readonly argv: ArgvDimension | undefined;
62
+ /** Parsed network dimension (raw patterns). */
63
+ readonly network: NetworkDimension | undefined;
64
+ /** Compiled branch dimension (patterns compiled; absent = no constraint). */
65
+ readonly branch: CompiledBranchDimension | undefined;
37
66
  readonly source: RuleEntryDoc;
38
67
  }
68
+ /** Compiled `branch` dimension: `target`/`remote` globs, raw `shared` flag. */
69
+ export interface CompiledBranchDimension {
70
+ readonly target: readonly CompiledPattern[];
71
+ readonly remote: readonly CompiledPattern[];
72
+ readonly shared: boolean;
73
+ }
39
74
  export interface CommandSpec {
40
75
  readonly word: CompiledPattern;
41
76
  readonly flag?: 'recursive' | 'force';
@@ -56,8 +91,33 @@ export declare class RuleError extends Error {
56
91
  }
57
92
  /** Parse a raw YAML permissions document; malformed files fail loud at load. */
58
93
  export declare function parsePermissionsDocument(text: string): PermissionsDoc;
94
+ /**
95
+ * Parse a permissions document given as an already-parsed object — the shape
96
+ * the DSH settings namespace stores (the JSON twin of the YAML file). Accepts
97
+ * both the bare `{ defaultAction, deny, allow, ask }` form and the file's
98
+ * `permissions:`-wrapped form. Malformed input fails loud, exactly like the
99
+ * YAML path, so a settings doc that cannot compile is a state the operator
100
+ * sees, never a silent ruleset change.
101
+ */
102
+ export declare function parsePermissionsObject(raw: unknown): PermissionsDoc;
103
+ /**
104
+ * Compile one parsed entry into a hot-path rule.
105
+ *
106
+ * Exported because the multi-file rule chain (`rule-chain.ts`) merges entries
107
+ * from several documents into one ruleset and must produce the SAME shape here
108
+ * — an entry built by hand would silently drop every compiled dimension, and an
109
+ * empty dimension means "no constraint", i.e. "matches everything".
110
+ */
111
+ export declare function compileRuleEntry(entry: RuleEntryDoc, action: RuleAction, index: number, opts?: CompileOptions): CompiledRuleEntry;
59
112
  /** Compile a validated document into hot-path rules. */
60
113
  export declare function compileDocument(doc: PermissionsDoc, opts?: CompileOptions): CompiledRuleset;
114
+ /**
115
+ * Compile rules given as a structured settings object (already parsed — no
116
+ * YAML in the loop). The settings-first half of the dual-source read path:
117
+ * `parsePermissionsObject` validates the JSON form exactly as strictly as the
118
+ * YAML path, then the standard `compileDocument` runs.
119
+ */
120
+ export declare function compileRulesObject(root: unknown, opts?: CompileOptions): CompiledRuleset;
61
121
  /** SHA-256 hash of the raw document (compile-cache key without recompiling). */
62
122
  export declare function documentHash(text: string): string;
63
123
  export declare function extractPathCandidates(args: Record<string, unknown>): string[];
package/lib/rule.js CHANGED
@@ -9,7 +9,8 @@
9
9
  * nothing matches, `defaultAction` applies.
10
10
  */
11
11
  import { parse } from 'yaml';
12
- import { compileGlob, hashText } from './compiler.js';
12
+ import { compileGlob, compileBranchPattern, hashText } from './compiler.js';
13
+ import { parseParamsDimension, parseAbsentDimension, parseAgentsDimension, parseWhenDimension, parseArgvDimension, parseNetworkDimension, parseBranchDimension, } from './rule-dims.js';
13
14
  const VALID_ACTIONS = ['allow', 'ask', 'deny'];
14
15
  const PATH_CANDIDATE_KEYS = [
15
16
  'path', 'paths', 'file', 'files', 'file_path', 'dir', 'directory', 'directories',
@@ -53,6 +54,17 @@ export function parsePermissionsDocument(text) {
53
54
  catch (error) {
54
55
  throw new RuleError(`invalid YAML: ${String(error)}`);
55
56
  }
57
+ return parsePermissionsObject(raw);
58
+ }
59
+ /**
60
+ * Parse a permissions document given as an already-parsed object — the shape
61
+ * the DSH settings namespace stores (the JSON twin of the YAML file). Accepts
62
+ * both the bare `{ defaultAction, deny, allow, ask }` form and the file's
63
+ * `permissions:`-wrapped form. Malformed input fails loud, exactly like the
64
+ * YAML path, so a settings doc that cannot compile is a state the operator
65
+ * sees, never a silent ruleset change.
66
+ */
67
+ export function parsePermissionsObject(raw) {
56
68
  if (raw === null || raw === undefined) {
57
69
  return { defaultAction: 'ask', deny: [], allow: [], ask: [] };
58
70
  }
@@ -82,7 +94,8 @@ export function parsePermissionsDocument(text) {
82
94
  function parseRuleEntry(raw, action, at) {
83
95
  if (!isRecord(raw))
84
96
  throw new RuleError(`${at} must be a mapping`);
85
- const unknown = Object.keys(raw).filter((k) => !['tools', 'command', 'args', 'paths', 'action', 'reason', 'enabled'].includes(k));
97
+ const VALID_KEYS = ['tools', 'command', 'args', 'paths', 'params', 'absent', 'agents', 'when', 'argv', 'network', 'branch', 'action', 'reason', 'enabled'];
98
+ const unknown = Object.keys(raw).filter((k) => !VALID_KEYS.includes(k));
86
99
  if (unknown.length > 0) {
87
100
  throw new RuleError(`${at} unknown field${unknown.length > 1 ? 's' : ''} ${unknown.map((k) => JSON.stringify(k)).join(', ')}`);
88
101
  }
@@ -98,6 +111,13 @@ function parseRuleEntry(raw, action, at) {
98
111
  command: stringList(raw.command, `${at}.command`),
99
112
  args: stringList(raw.args, `${at}.args`),
100
113
  paths: stringList(raw.paths, `${at}.paths`),
114
+ params: parseParamsDimension(raw.params, `${at}.params`),
115
+ absent: parseAbsentDimension(raw.absent, `${at}.absent`),
116
+ agents: parseAgentsDimension(raw.agents, `${at}.agents`),
117
+ when: parseWhenDimension(raw.when, `${at}.when`),
118
+ argv: parseArgvDimension(raw.argv, `${at}.argv`),
119
+ network: parseNetworkDimension(raw.network, `${at}.network`),
120
+ branch: parseBranchDimension(raw.branch, `${at}.branch`),
101
121
  action,
102
122
  reason: reason === undefined ? `${action}` : reason,
103
123
  enabled: raw.enabled === undefined ? true : typeof raw.enabled === 'boolean' ? raw.enabled : (() => { throw new RuleError(`${at}.enabled must be a boolean`); })(),
@@ -133,10 +153,32 @@ function compileCommand(entry, opts) {
133
153
  }
134
154
  return { word: compileGlob(word, { segments: false, maxStars: opts.maxGlobStars ?? 2 }), flag };
135
155
  }
136
- /** Compile a validated document into hot-path rules. */
137
- export function compileDocument(doc, opts = {}) {
138
- const comp = (list, action, offset) => list.map((entry, i) => ({
139
- index: offset + i,
156
+ /**
157
+ * Compile the `branch` dimension (undefined = the rule does not constrain on it).
158
+ *
159
+ * Glob degree is bounded inside `compileBranchPattern` (DEFAULT_MAX_STARS), so
160
+ * this takes no options: branch patterns are never path-segmented.
161
+ */
162
+ function compileBranchDimension(dim) {
163
+ if (dim === undefined)
164
+ return undefined;
165
+ return {
166
+ target: (dim.target ?? []).map((p) => compileBranchPattern(p)),
167
+ remote: (dim.remote ?? []).map((p) => compileBranchPattern(p)),
168
+ shared: dim.shared === true,
169
+ };
170
+ }
171
+ /**
172
+ * Compile one parsed entry into a hot-path rule.
173
+ *
174
+ * Exported because the multi-file rule chain (`rule-chain.ts`) merges entries
175
+ * from several documents into one ruleset and must produce the SAME shape here
176
+ * — an entry built by hand would silently drop every compiled dimension, and an
177
+ * empty dimension means "no constraint", i.e. "matches everything".
178
+ */
179
+ export function compileRuleEntry(entry, action, index, opts = {}) {
180
+ return {
181
+ index,
140
182
  action,
141
183
  reason: entry.reason,
142
184
  enabled: entry.enabled,
@@ -144,8 +186,19 @@ export function compileDocument(doc, opts = {}) {
144
186
  command: entry.command.map((c) => compileCommand(c, opts)),
145
187
  args: compilePatternList(entry.args, 'args', opts),
146
188
  paths: compilePatternList(entry.paths, 'paths', opts),
189
+ params: entry.params,
190
+ absent: entry.absent,
191
+ agents: entry.agents,
192
+ when: entry.when,
193
+ argv: entry.argv,
194
+ network: entry.network,
195
+ branch: compileBranchDimension(entry.branch),
147
196
  source: entry,
148
- }));
197
+ };
198
+ }
199
+ /** Compile a validated document into hot-path rules. */
200
+ export function compileDocument(doc, opts = {}) {
201
+ const comp = (list, action, offset) => list.map((entry, i) => compileRuleEntry(entry, action, offset + i, opts));
149
202
  return {
150
203
  defaultAction: doc.defaultAction,
151
204
  deny: comp(doc.deny, 'deny', 0),
@@ -154,6 +207,15 @@ export function compileDocument(doc, opts = {}) {
154
207
  caseInsensitivePaths: opts.caseInsensitivePaths ?? false,
155
208
  };
156
209
  }
210
+ /**
211
+ * Compile rules given as a structured settings object (already parsed — no
212
+ * YAML in the loop). The settings-first half of the dual-source read path:
213
+ * `parsePermissionsObject` validates the JSON form exactly as strictly as the
214
+ * YAML path, then the standard `compileDocument` runs.
215
+ */
216
+ export function compileRulesObject(root, opts = {}) {
217
+ return compileDocument(parsePermissionsObject(root), opts);
218
+ }
157
219
  /** SHA-256 hash of the raw document (compile-cache key without recompiling). */
158
220
  export function documentHash(text) {
159
221
  return hashText(text);
@@ -0,0 +1,52 @@
1
+ import { type RuleAction } from './rule.js';
2
+ /** Rules per action, so the panel can say "3 deny / 7 allow / 1 ask" at a glance. */
3
+ export interface RulesViewCounts {
4
+ readonly allow: number;
5
+ readonly deny: number;
6
+ readonly ask: number;
7
+ }
8
+ /** The whole read-only face of one permissions document. */
9
+ export interface RulesView {
10
+ /** Absolute path, or `''` when the gate has no rules file configured. */
11
+ readonly path: string;
12
+ readonly exists: boolean;
13
+ /** File size on disk, not the length of `raw` — a truncated view still reports the truth. */
14
+ readonly bytes: number;
15
+ /** Content hash of the text the gate would load, for correlating with a reload. */
16
+ readonly hash: string;
17
+ readonly lines: number;
18
+ /** The document itself. Empty when absent, unreadable, or truncated away. */
19
+ readonly raw: string;
20
+ readonly truncated: boolean;
21
+ /** Parsed only when the document compiled; absent when it did not. */
22
+ readonly defaultAction?: RuleAction;
23
+ readonly counts: RulesViewCounts;
24
+ /** Why the document could not be shown or parsed. Absent when all is well. */
25
+ readonly error?: string;
26
+ /** Where the rules were read from. Absent on views produced before the settings migration. */
27
+ readonly source?: 'file' | 'settings';
28
+ /** The structured rules object the settings namespace stores (settings source only). */
29
+ readonly rules?: unknown;
30
+ }
31
+ /**
32
+ * Read one permissions document for display.
33
+ *
34
+ * Never throws: a missing, unreadable or malformed file is a state the panel has
35
+ * to render, not an error that should blank the section. Every failure mode is
36
+ * reported in the returned value instead.
37
+ */
38
+ export declare function readRulesView(rulesFile: string): RulesView;
39
+ /** The pseudo-path a settings view reports (there is no file to name). */
40
+ export declare const SETTINGS_RULES_VIEW_PATH = "settings:dsh-perm-gate-rules";
41
+ /**
42
+ * Render the settings-sourced rules for display — the read-only face of the
43
+ * `dsh-perm-gate-rules` namespace, the twin of {@link readRulesView}.
44
+ *
45
+ * The YAML rendering of the structured object fills `raw`, so the settings card
46
+ * shows the document in the same format the file view used; `rules` carries the
47
+ * structured object itself and `source: 'settings'` states the provenance.
48
+ *
49
+ * Never throws: a doc that fails `compileRulesObject` is a state the panel has
50
+ * to render (the `error` field), not an error that should blank the section.
51
+ */
52
+ export declare function readRulesViewFromSettings(rules: unknown): RulesView;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Reading the permissions YAML, for the panel that shows it.
3
+ *
4
+ * The card could already WRITE the rules file — the allowlist section appends
5
+ * and replaces patterns, and `index.ts` mirrors those edits back into the
6
+ * document. Nothing could read it back, so an operator editing patterns through
7
+ * the panel had no way to see the document they were editing: the file the gate
8
+ * actually loads was invisible from the UI.
9
+ *
10
+ * This is the missing read half. It is deliberately **read-only** — a view must
11
+ * never be able to change the ruleset it is describing, or "let me look at the
12
+ * rules" becomes a risky action. Writing stays where it already lives
13
+ * (`allowlist.ts`, the settings namespace).
14
+ */
15
+ import { readFileSync, statSync } from 'node:fs';
16
+ import { stringify } from 'yaml';
17
+ import { RuleError, compileDocument, compileRulesObject, documentHash, parsePermissionsDocument } from './rule.js';
18
+ /** Beyond this the panel would be rendering a log, not a rules file. */
19
+ const MAX_VIEW_BYTES = 512 * 1024;
20
+ const NO_COUNTS = { allow: 0, deny: 0, ask: 0 };
21
+ /**
22
+ * Read one permissions document for display.
23
+ *
24
+ * Never throws: a missing, unreadable or malformed file is a state the panel has
25
+ * to render, not an error that should blank the section. Every failure mode is
26
+ * reported in the returned value instead.
27
+ */
28
+ export function readRulesView(rulesFile) {
29
+ if (rulesFile === '') {
30
+ return {
31
+ path: '', exists: false, bytes: 0, hash: '', lines: 0, raw: '', truncated: false,
32
+ counts: NO_COUNTS, error: 'no rules file is configured',
33
+ };
34
+ }
35
+ let bytes = 0;
36
+ let exists = true;
37
+ try {
38
+ bytes = statSync(rulesFile).size;
39
+ }
40
+ catch {
41
+ exists = false;
42
+ }
43
+ let raw = '';
44
+ try {
45
+ raw = readFileSync(rulesFile, 'utf8');
46
+ }
47
+ catch (e) {
48
+ return {
49
+ path: rulesFile, exists, bytes, hash: '', lines: 0, raw: '', truncated: false,
50
+ counts: NO_COUNTS,
51
+ error: exists ? `unreadable: ${String(e?.message ?? e)}` : 'file does not exist',
52
+ };
53
+ }
54
+ const lines = raw === '' ? 0 : raw.split('\n').length;
55
+ const truncated = raw.length > MAX_VIEW_BYTES;
56
+ const shown = truncated ? raw.slice(0, MAX_VIEW_BYTES) : raw;
57
+ // Parsing is what makes the view worth having: a YAML file that loads as text
58
+ // but not as rules is exactly the failure the panel exists to expose.
59
+ try {
60
+ const doc = parsePermissionsDocument(raw);
61
+ const ruleset = compileDocument(doc);
62
+ return {
63
+ path: rulesFile, exists: true, bytes, hash: documentHash(raw), lines,
64
+ raw: shown, truncated,
65
+ defaultAction: ruleset.defaultAction,
66
+ counts: { allow: ruleset.allow.length, deny: ruleset.deny.length, ask: ruleset.ask.length },
67
+ source: 'file',
68
+ };
69
+ }
70
+ catch (e) {
71
+ const detail = e instanceof RuleError ? e.message : String(e?.message ?? e);
72
+ return {
73
+ path: rulesFile, exists: true, bytes, hash: documentHash(raw), lines,
74
+ raw: shown, truncated,
75
+ counts: NO_COUNTS,
76
+ error: `does not compile: ${detail}`,
77
+ source: 'file',
78
+ };
79
+ }
80
+ }
81
+ /** The pseudo-path a settings view reports (there is no file to name). */
82
+ export const SETTINGS_RULES_VIEW_PATH = 'settings:dsh-perm-gate-rules';
83
+ /**
84
+ * Render the settings-sourced rules for display — the read-only face of the
85
+ * `dsh-perm-gate-rules` namespace, the twin of {@link readRulesView}.
86
+ *
87
+ * The YAML rendering of the structured object fills `raw`, so the settings card
88
+ * shows the document in the same format the file view used; `rules` carries the
89
+ * structured object itself and `source: 'settings'` states the provenance.
90
+ *
91
+ * Never throws: a doc that fails `compileRulesObject` is a state the panel has
92
+ * to render (the `error` field), not an error that should blank the section.
93
+ */
94
+ export function readRulesViewFromSettings(rules) {
95
+ let raw = '';
96
+ try {
97
+ raw = stringify(rules ?? {});
98
+ }
99
+ catch {
100
+ raw = '';
101
+ }
102
+ const lines = raw === '' ? 0 : raw.split('\n').length;
103
+ const hash = documentHash(JSON.stringify(rules ?? {}));
104
+ try {
105
+ const ruleset = compileRulesObject(rules);
106
+ return {
107
+ path: SETTINGS_RULES_VIEW_PATH, exists: true, bytes: raw.length, hash, lines,
108
+ raw, truncated: false,
109
+ defaultAction: ruleset.defaultAction,
110
+ counts: { allow: ruleset.allow.length, deny: ruleset.deny.length, ask: ruleset.ask.length },
111
+ source: 'settings',
112
+ rules,
113
+ };
114
+ }
115
+ catch (e) {
116
+ const detail = e instanceof RuleError ? e.message : String(e?.message ?? e);
117
+ return {
118
+ path: SETTINGS_RULES_VIEW_PATH, exists: true, bytes: raw.length, hash, lines,
119
+ raw, truncated: false,
120
+ counts: NO_COUNTS,
121
+ error: `does not compile: ${detail}`,
122
+ source: 'settings',
123
+ rules,
124
+ };
125
+ }
126
+ }