depcurfew 1.1.0 → 1.3.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/README.md +389 -58
- package/dist/cli.js +43 -6
- package/dist/index.d.ts +1226 -135
- package/dist/index.js +3 -1
- package/dist/io-eBMDYKV0.js +52 -0
- package/dist/run-DYkP4HLQ.js +46946 -0
- package/package.json +5 -2
- package/schema.json +41 -0
- package/dist/run-CvdVkFPn.js +0 -28211
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
headers: Record<string, string>;
|
|
3
|
-
signal: AbortSignal;
|
|
4
|
-
redirect?: 'manual';
|
|
5
|
-
}) => Promise<Response>;
|
|
6
|
-
declare const EXIT_OK = 0;
|
|
7
|
-
declare const EXIT_FAILURE = 1;
|
|
8
|
-
declare const EXIT_USAGE = 2;
|
|
9
|
-
interface CliIo {
|
|
10
|
-
stdout(text: string): void;
|
|
11
|
-
stderr(text: string): void;
|
|
12
|
-
/** Whether each stream may be colored (see color.ts); absent means plain text. */
|
|
13
|
-
colors?: {
|
|
14
|
-
stdout: boolean;
|
|
15
|
-
stderr: boolean;
|
|
16
|
-
};
|
|
17
|
-
/** Whether each stream is a terminal; absent means it is not. */
|
|
18
|
-
terminal?: {
|
|
19
|
-
stdout: boolean;
|
|
20
|
-
stderr: boolean;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
interface CheckDependencies {
|
|
24
|
-
fetch: FetchLike;
|
|
25
|
-
now: () => Date;
|
|
26
|
-
/** The whole of stdin, for hook mode. */
|
|
27
|
-
stdin: () => Promise<string>;
|
|
28
|
-
/** The user's home directory, with the user's .npmrc. */
|
|
29
|
-
home?: () => string;
|
|
30
|
-
}
|
|
31
|
-
declare function run(argv: readonly string[], output: CliIo, dependencies?: CheckDependencies): Promise<number>;
|
|
32
|
-
declare function getVersion(): string;
|
|
1
|
+
import { Readable, Writable } from "node:stream";
|
|
33
2
|
type ToolId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno' | 'uv' | 'pip' | 'poetry' | 'pdm' | 'pipenv' | 'renovate' | 'dependabot';
|
|
34
3
|
type ToolKind = 'package-manager' | 'update-bot';
|
|
35
4
|
/** The registry a package manager installs from; package names in the policy are compared per ecosystem. */
|
|
@@ -129,6 +98,129 @@ interface ProjectKnowledge {
|
|
|
129
98
|
/** Key of the package list when the workspace declaration is an object instead of a list; `null` if only lists are accepted. */
|
|
130
99
|
workspacesObjectKey: Fact<string | null>;
|
|
131
100
|
}
|
|
101
|
+
/** A platform family for user-level paths. */
|
|
102
|
+
type UserPlatform = 'linux' | 'darwin' | 'win32';
|
|
103
|
+
/** A base directory for a user-level file. */
|
|
104
|
+
interface UserDirectory {
|
|
105
|
+
/**
|
|
106
|
+
* Where it starts: an environment variable (used only when it is set and not empty, and with `absolute` only when
|
|
107
|
+
* it is an absolute path), or the home directory (HOME on Linux and macOS, USERPROFILE on Windows). With
|
|
108
|
+
* `relativeToCwd` the tool takes a relative value as it is, so it reads the file relative to the directory each
|
|
109
|
+
* command runs in; without it and without `absolute`, what the tool does with a relative value is not known.
|
|
110
|
+
*/
|
|
111
|
+
from: {
|
|
112
|
+
env: string;
|
|
113
|
+
absolute?: boolean;
|
|
114
|
+
relativeToCwd?: boolean;
|
|
115
|
+
emptyCounts?: boolean;
|
|
116
|
+
} | 'home';
|
|
117
|
+
/** Path below it, with / separators; empty for the directory itself. */
|
|
118
|
+
path: string;
|
|
119
|
+
/** Use this directory only when it already exists (pip on macOS). */
|
|
120
|
+
ifExists?: boolean;
|
|
121
|
+
/** Skip this directory while this environment variable is set and not empty (pip 26.2+ on macOS and XDG_DATA_HOME). */
|
|
122
|
+
unlessEnv?: string;
|
|
123
|
+
}
|
|
124
|
+
/** Where the user file is on some platforms: the first directory that applies is used. */
|
|
125
|
+
interface UserLocation {
|
|
126
|
+
platforms: readonly UserPlatform[];
|
|
127
|
+
directories: readonly UserDirectory[];
|
|
128
|
+
file: string;
|
|
129
|
+
}
|
|
130
|
+
/** The user-level configuration file of a range of versions. */
|
|
131
|
+
interface UserFile {
|
|
132
|
+
format: 'ini' | 'yaml' | 'toml';
|
|
133
|
+
locations: readonly UserLocation[];
|
|
134
|
+
/** INI only: the [sections] whose keys the tool reads, '' for the keys above the first header; absent: every key. */
|
|
135
|
+
sections?: readonly string[];
|
|
136
|
+
/** Other user-level files this version reads the same settings from, whose keys win over this file's. */
|
|
137
|
+
overriddenBy?: readonly UserFile[];
|
|
138
|
+
/** INI only: the tool reads a key with underscores as the same key with dashes (pip: only_binary is only-binary). */
|
|
139
|
+
underscoresAsDashes?: boolean;
|
|
140
|
+
/** YAML only: the tool's loader applies merge keys (<<: *defaults), so merged keys count as keys of the map. */
|
|
141
|
+
mergeKeys?: boolean;
|
|
142
|
+
}
|
|
143
|
+
/** What depcurfew writes with a user-level setting. */
|
|
144
|
+
type UserSettingPurpose = 'cooldown' | 'cooldownExclude' | 'cooldownStrict' | 'scriptsAllow' | 'scriptsStrict' | 'buildsDeny' | 'buildsAllow' | 'sourceGit' | 'sourceRemote' | 'sourceFile' | 'sourceDirectory' | 'sourcesExotic';
|
|
145
|
+
/** A setting the tool reads from its user file: the key there and the versions that read it from that file. */
|
|
146
|
+
interface UserSetting {
|
|
147
|
+
purpose: UserSettingPurpose;
|
|
148
|
+
key: string;
|
|
149
|
+
since: string;
|
|
150
|
+
/** First version that no longer reads it from the user file. */
|
|
151
|
+
until?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The values of a switch that turns a protection off, given by the values the tool reads as off: every other value
|
|
155
|
+
* turns it on (fail closed), so a string "false" or "yes" that the tool takes for true is not missed. In INI files and
|
|
156
|
+
* environment variables values are compared as text after trimming, elsewhere with their type (false, 0, null, "").
|
|
157
|
+
*/
|
|
158
|
+
interface UnlessOff<T> {
|
|
159
|
+
except: readonly T[];
|
|
160
|
+
}
|
|
161
|
+
/** A value in the user file that weakens every project that does not set the key itself (doctor --user). */
|
|
162
|
+
interface UserWeakening {
|
|
163
|
+
key: string;
|
|
164
|
+
/** 'any' for every value, the values that weaken, or every value except those the tool reads as off. */
|
|
165
|
+
values: 'any' | readonly (string | number | boolean)[] | UnlessOff<string | number | boolean | null>;
|
|
166
|
+
/** What it does, for the message. */
|
|
167
|
+
effect: string;
|
|
168
|
+
}
|
|
169
|
+
/** An environment variable that bypasses or overrides the user file or the policy (doctor --user). */
|
|
170
|
+
interface UserVariable {
|
|
171
|
+
/** The variable name; matched without regard to case when `anyCase`. */
|
|
172
|
+
name: string;
|
|
173
|
+
anyCase?: boolean;
|
|
174
|
+
/** 'any' when every value matters, the values that weaken, or every value except those the tool reads as off. */
|
|
175
|
+
values: 'any' | readonly string[] | UnlessOff<string>;
|
|
176
|
+
effect: string;
|
|
177
|
+
/** First version that reads the variable; every version when absent. */
|
|
178
|
+
since?: string;
|
|
179
|
+
/** First version that no longer reads it. */
|
|
180
|
+
until?: string;
|
|
181
|
+
/** The file the tool reads when the variable is unset, from the home directory (~/.npmrc): naming it changes nothing. */
|
|
182
|
+
defaultPath?: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Directories of the user file that a library the tool calls decides, by the library's version (platformdirs for
|
|
186
|
+
* Poetry and PDM on macOS). depcurfew reads that version from the library's .dist-info next to the tool's own.
|
|
187
|
+
*/
|
|
188
|
+
interface LibraryDirectories {
|
|
189
|
+
/** The distribution name of the library, as its .dist-info folder spells it (platformdirs). */
|
|
190
|
+
library: string;
|
|
191
|
+
/** The platforms whose directories it decides; the other platforms keep those of `files`. */
|
|
192
|
+
platforms: readonly UserPlatform[];
|
|
193
|
+
/** The directories per library version, oldest first; they replace the directories of `files` on those platforms. */
|
|
194
|
+
directories: Versioned<readonly UserDirectory[]>;
|
|
195
|
+
}
|
|
196
|
+
/** The unit of the release-age setting in the user file, when it differs from the tool's own (`cooldown.unit`). */
|
|
197
|
+
interface UserCooldownUnit {
|
|
198
|
+
key: string;
|
|
199
|
+
unit: 'seconds' | 'minutes' | 'hours' | 'days';
|
|
200
|
+
/** Only whole numbers count; depcurfew rounds up, to the longer delay. */
|
|
201
|
+
wholeNumbers: boolean;
|
|
202
|
+
/** What the tool does with any other value: ignores it without a word, or stops. */
|
|
203
|
+
otherValues: 'ignored' | 'fail';
|
|
204
|
+
}
|
|
205
|
+
/** The tool's user-level configuration, for `depcurfew apply --user` and `doctor --user`. */
|
|
206
|
+
interface UserConfigKnowledge {
|
|
207
|
+
/** The user file per version, oldest first; null for versions without a user file. */
|
|
208
|
+
files: Fact<Versioned<UserFile | null>>;
|
|
209
|
+
/** Directories of the user file that depend on the version of a library the tool calls. */
|
|
210
|
+
libraryDirectories?: Fact<LibraryDirectories>;
|
|
211
|
+
/** The unit of the release-age setting there, when it is not the unit of the tool's project setting. */
|
|
212
|
+
cooldownUnit?: Fact<UserCooldownUnit>;
|
|
213
|
+
/** Settings depcurfew can write there. */
|
|
214
|
+
settings: Fact<readonly UserSetting[]>;
|
|
215
|
+
/** What a version does with a key it does not know in the user file: ignore it, warn, or fail. */
|
|
216
|
+
unknownKeys: Fact<'ignore' | 'warn' | 'fail'>;
|
|
217
|
+
/** Whether a value the installed version cannot parse makes every command fail; depcurfew then needs the exact version. */
|
|
218
|
+
invalidValueFails: Fact<boolean>;
|
|
219
|
+
/** Values in the user file that weaken projects. */
|
|
220
|
+
weakening: Fact<readonly UserWeakening[]>;
|
|
221
|
+
/** Environment variables that move or skip the user file or override settings. */
|
|
222
|
+
variables: Fact<readonly UserVariable[]>;
|
|
223
|
+
}
|
|
132
224
|
interface ToolKnowledge {
|
|
133
225
|
id: ToolId;
|
|
134
226
|
name: string;
|
|
@@ -141,9 +233,27 @@ interface ToolKnowledge {
|
|
|
141
233
|
sources?: SourcesKnowledge;
|
|
142
234
|
/** Package managers only: how to recognize the tool in a repository. */
|
|
143
235
|
project?: ProjectKnowledge;
|
|
236
|
+
/** Package managers only: the user-level configuration (`--user`). */
|
|
237
|
+
user?: UserConfigKnowledge;
|
|
144
238
|
/** Further sourced facts that matter for generators and `doctor`, keyed by a short name. */
|
|
145
239
|
extra?: Readonly<Record<string, Fact<unknown>>>;
|
|
146
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* The values with which a switch weakens a protection: `delay` is a release-age delay in the tool's unit
|
|
243
|
+
* (cooldown.unit) or a duration string (durationStrings), which depcurfew compares with the policy; `any` is every
|
|
244
|
+
* value; otherwise the values listed, or every value except those (fail closed). A flag without a value is "true".
|
|
245
|
+
*/
|
|
246
|
+
type OverrideValues = 'delay' | 'any' | readonly string[] | {
|
|
247
|
+
readonly except: readonly string[];
|
|
248
|
+
};
|
|
249
|
+
/** An option or environment variable of one command that weakens what depcurfew apply configured. */
|
|
250
|
+
interface OverrideSwitch {
|
|
251
|
+
/** The option as the scanner resolves it (--name) or the variable, compared without regard to case. */
|
|
252
|
+
name: string;
|
|
253
|
+
/** The release-age delay, or the rule on dependency install scripts. */
|
|
254
|
+
weakens: 'delay' | 'scripts';
|
|
255
|
+
values: OverrideValues;
|
|
256
|
+
}
|
|
147
257
|
/** A tool that environment facts refer to: Node.js, Python or a package manager. */
|
|
148
258
|
type RuntimeOrTool = 'node' | 'python' | ToolId;
|
|
149
259
|
interface PackageManagerFieldKnowledge {
|
|
@@ -220,6 +330,18 @@ interface NpmRegistryKnowledge {
|
|
|
220
330
|
installScripts: readonly string[];
|
|
221
331
|
}
|
|
222
332
|
/** How the Python interpreter runs a module (python -m pip) or a command string. */
|
|
333
|
+
/**
|
|
334
|
+
* A Python virtual environment as venv creates it: `marker` is the file in the environment's directory, and the
|
|
335
|
+
* activation scripts sit in a binary directory right under it, per platform (bin on POSIX, Scripts on Windows).
|
|
336
|
+
*/
|
|
337
|
+
interface VirtualEnvironmentKnowledge {
|
|
338
|
+
marker: string;
|
|
339
|
+
scripts: readonly {
|
|
340
|
+
platform: 'posix' | 'windows';
|
|
341
|
+
directory: string;
|
|
342
|
+
names: readonly string[];
|
|
343
|
+
}[];
|
|
344
|
+
}
|
|
223
345
|
interface PythonInterpreterKnowledge {
|
|
224
346
|
/** Executable names; versioned names such as python3.12 are the same. */
|
|
225
347
|
executables: readonly string[];
|
|
@@ -310,11 +432,172 @@ interface ClaudeCodeHooksKnowledge {
|
|
|
310
432
|
args: string;
|
|
311
433
|
};
|
|
312
434
|
}
|
|
435
|
+
/** additionalContext in hookSpecificOutput of a PreToolUse hook: text Claude reads next to the tool result. */
|
|
436
|
+
interface ClaudeCodeHookContextKnowledge {
|
|
437
|
+
field: string;
|
|
438
|
+
/** Whether the field works without a permission decision (which leaves the call to the normal permission flow). */
|
|
439
|
+
withoutDecision: boolean;
|
|
440
|
+
/** Longer text is written to a file and Claude gets its path with a preview. */
|
|
441
|
+
inlineLimit: number;
|
|
442
|
+
}
|
|
443
|
+
/** The SessionStart hook that can install the project, and what happens to a guard whose script is missing. */
|
|
444
|
+
interface ClaudeCodeSessionStartKnowledge {
|
|
445
|
+
event: string;
|
|
446
|
+
/** The matchers for a new and a resumed session. */
|
|
447
|
+
matchers: readonly string[];
|
|
448
|
+
/** The variable a shell-form hook command reads for the project root. */
|
|
449
|
+
projectDirVariable: string;
|
|
450
|
+
/** Whether Claude's first response waits for SessionStart hooks to finish. */
|
|
451
|
+
firstResponseWaits: boolean;
|
|
452
|
+
/** What a PreToolUse hook with onFailure "block" does when its script is missing. */
|
|
453
|
+
missingScriptWithBlock: 'blocks';
|
|
454
|
+
/** The part of the error Claude Code shows then, before the output of the command that failed. */
|
|
455
|
+
blockedNotice: string;
|
|
456
|
+
/** Whether a cloud environment's setup script runs before Claude Code starts. */
|
|
457
|
+
cloudSetupScriptRunsBeforeClaudeCode: boolean;
|
|
458
|
+
}
|
|
313
459
|
/** A Claude Code tool that runs shell commands, by the name a matcher and the event use, and the shell it runs them in. */
|
|
314
460
|
interface ClaudeCodeShellTool {
|
|
315
461
|
tool: string;
|
|
316
462
|
shell: 'bash' | 'powershell';
|
|
317
463
|
}
|
|
464
|
+
/** A shell tool call that carries another input in place of the command (a Monitor WebSocket watch). */
|
|
465
|
+
interface ClaudeCodeCommandlessInputKnowledge {
|
|
466
|
+
tool: string;
|
|
467
|
+
/** The field of the event, next to the command field, that takes the place of the command. */
|
|
468
|
+
input: string;
|
|
469
|
+
}
|
|
470
|
+
/** Model Context Protocol revisions: the legacy ones open a session with initialize, the modern ones carry the version on every request. */
|
|
471
|
+
interface McpRevisionsKnowledge {
|
|
472
|
+
/** Newest first. */
|
|
473
|
+
legacy: readonly string[];
|
|
474
|
+
/** Newest first. */
|
|
475
|
+
modern: readonly string[];
|
|
476
|
+
}
|
|
477
|
+
/** The initialize handshake of the legacy revisions. */
|
|
478
|
+
interface McpLifecycleKnowledge {
|
|
479
|
+
initialize: string;
|
|
480
|
+
initialized: string;
|
|
481
|
+
ping: string;
|
|
482
|
+
/** Fields of the initialize request and result. */
|
|
483
|
+
request: {
|
|
484
|
+
protocolVersion: string;
|
|
485
|
+
capabilities: string;
|
|
486
|
+
clientInfo: string;
|
|
487
|
+
};
|
|
488
|
+
result: {
|
|
489
|
+
protocolVersion: string;
|
|
490
|
+
capabilities: string;
|
|
491
|
+
serverInfo: string;
|
|
492
|
+
instructions: string;
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/** What a modern (stateless) request and result carry in `_meta`, and the discovery method. */
|
|
496
|
+
interface McpStatelessKnowledge {
|
|
497
|
+
metaField: string;
|
|
498
|
+
protocolVersion: string;
|
|
499
|
+
clientCapabilities: string;
|
|
500
|
+
clientInfo: string;
|
|
501
|
+
serverInfo: string;
|
|
502
|
+
discover: string;
|
|
503
|
+
/** The fields of a discovery result. */
|
|
504
|
+
discoverResult: {
|
|
505
|
+
supportedVersions: string;
|
|
506
|
+
capabilities: string;
|
|
507
|
+
instructions: string;
|
|
508
|
+
};
|
|
509
|
+
/** Every modern result has this field, with `complete` for a finished result. */
|
|
510
|
+
resultType: {
|
|
511
|
+
field: string;
|
|
512
|
+
complete: string;
|
|
513
|
+
};
|
|
514
|
+
/** Caching hints that discovery and tools/list results must carry. */
|
|
515
|
+
caching: {
|
|
516
|
+
ttl: string;
|
|
517
|
+
scope: string;
|
|
518
|
+
publicScope: string;
|
|
519
|
+
};
|
|
520
|
+
/** The data of an unsupported-version error. */
|
|
521
|
+
unsupportedVersionData: {
|
|
522
|
+
supported: string;
|
|
523
|
+
requested: string;
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
/** Tools: the methods and the fields of a tool and of a call result. */
|
|
527
|
+
interface McpToolsKnowledge {
|
|
528
|
+
list: string;
|
|
529
|
+
call: string;
|
|
530
|
+
capability: string;
|
|
531
|
+
/** The revision that added each optional field of a tool or a result; older clients do not know them. */
|
|
532
|
+
fieldsSince: {
|
|
533
|
+
annotations: string;
|
|
534
|
+
title: string;
|
|
535
|
+
structuredContent: string;
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
interface McpCancellationKnowledge {
|
|
539
|
+
method: string;
|
|
540
|
+
requestId: string;
|
|
541
|
+
}
|
|
542
|
+
/** The JSON-RPC error codes the server uses. */
|
|
543
|
+
interface McpErrorCodesKnowledge {
|
|
544
|
+
parseError: number;
|
|
545
|
+
invalidRequest: number;
|
|
546
|
+
methodNotFound: number;
|
|
547
|
+
invalidParams: number;
|
|
548
|
+
internalError: number;
|
|
549
|
+
unsupportedProtocolVersion: number;
|
|
550
|
+
}
|
|
551
|
+
/** The id of an error response: required (a string or a number) in the schemas before `optionalSince`, optional from it. */
|
|
552
|
+
interface McpErrorIdKnowledge {
|
|
553
|
+
optionalSince: string;
|
|
554
|
+
}
|
|
555
|
+
/** stdio: messages are UTF-8 JSON-RPC, one per line, without embedded newlines; stderr is free for logs. */
|
|
556
|
+
interface McpStdioKnowledge {
|
|
557
|
+
delimiter: string;
|
|
558
|
+
/** Revisions whose servers must accept JSON-RPC batches (arrays). */
|
|
559
|
+
batchRevisions: readonly string[];
|
|
560
|
+
}
|
|
561
|
+
/** How Claude Code is told about a stdio server: `claude mcp add` and the project file. */
|
|
562
|
+
interface McpClaudeCodeKnowledge {
|
|
563
|
+
/** claude mcp add [options] <name> -- <command> [args...] */
|
|
564
|
+
addCommand: readonly string[];
|
|
565
|
+
separator: string;
|
|
566
|
+
scopeOption: string;
|
|
567
|
+
projectScope: string;
|
|
568
|
+
transportOption: string;
|
|
569
|
+
stdioTransport: string;
|
|
570
|
+
projectFile: string;
|
|
571
|
+
serversKey: string;
|
|
572
|
+
/** Set in the server's environment to the project root; in command and args it needs a default. */
|
|
573
|
+
projectDirVariable: string;
|
|
574
|
+
projectDirReference: string;
|
|
575
|
+
}
|
|
576
|
+
/** Where Claude Desktop keeps its configuration, per platform, and the key of its servers. */
|
|
577
|
+
interface McpClaudeDesktopKnowledge {
|
|
578
|
+
configFiles: {
|
|
579
|
+
darwin: string;
|
|
580
|
+
win32: string;
|
|
581
|
+
linux: string;
|
|
582
|
+
};
|
|
583
|
+
serversKey: string;
|
|
584
|
+
}
|
|
585
|
+
/** VS Code's own format (.vscode/mcp.json) and its command line. */
|
|
586
|
+
interface McpVsCodeKnowledge {
|
|
587
|
+
file: string;
|
|
588
|
+
serversKey: string;
|
|
589
|
+
typeField: string;
|
|
590
|
+
stdioType: string;
|
|
591
|
+
cwdField: string;
|
|
592
|
+
workspaceFolder: string;
|
|
593
|
+
/** code --add-mcp '{"name": …, …}' adds a server to the user profile. */
|
|
594
|
+
addOption: string;
|
|
595
|
+
}
|
|
596
|
+
interface McpClientsKnowledge {
|
|
597
|
+
claudeCode: Fact<McpClaudeCodeKnowledge>;
|
|
598
|
+
claudeDesktop: Fact<McpClaudeDesktopKnowledge>;
|
|
599
|
+
vscode: Fact<McpVsCodeKnowledge>;
|
|
600
|
+
}
|
|
318
601
|
/**
|
|
319
602
|
* Annotation workflow commands: `::{command} {property}={value},…::{message}` on a line of its own. Values are
|
|
320
603
|
* escaped by replacing each character listed in `escape` (in order), properties also with `escapeProperty`.
|
|
@@ -339,6 +622,44 @@ interface AnnotationLimitsKnowledge {
|
|
|
339
622
|
perJob: number;
|
|
340
623
|
}
|
|
341
624
|
/** The Markdown job summary of a step: the environment variable naming its file and its size limit. */
|
|
625
|
+
/** Creating a check run with the workflow token, so that the result shows in the pull request's Checks tab. */
|
|
626
|
+
interface CheckRunsKnowledge {
|
|
627
|
+
/**
|
|
628
|
+
* Paths with {repository} as owner/name: create (POST), update (PATCH, {id} the check run), and list the check runs
|
|
629
|
+
* of a commit (GET, {ref} a SHA), which takes the query parameters checkName and filter (with the value latest).
|
|
630
|
+
*/
|
|
631
|
+
endpoints: {
|
|
632
|
+
create: string;
|
|
633
|
+
update: string;
|
|
634
|
+
list: string;
|
|
635
|
+
};
|
|
636
|
+
query: {
|
|
637
|
+
checkName: string;
|
|
638
|
+
filter: string;
|
|
639
|
+
latest: string;
|
|
640
|
+
};
|
|
641
|
+
apiVersion: string;
|
|
642
|
+
/** The workflow permission the token needs; it covers the list endpoint too (write includes read). */
|
|
643
|
+
permission: Record<string, string>;
|
|
644
|
+
/** Limit of output.summary, in characters. */
|
|
645
|
+
summaryMaxLength: number;
|
|
646
|
+
/** output.annotations: at most this many per request; further ones are appended by update requests. */
|
|
647
|
+
annotationsPerRequest: number;
|
|
648
|
+
/** annotation_level for each doctor level. */
|
|
649
|
+
annotationLevels: Record<'error' | 'warning' | 'note', string>;
|
|
650
|
+
/** Limits of an annotation: title in characters, message in bytes (64 KB, taken as 64000). */
|
|
651
|
+
annotationTitleMaxLength: number;
|
|
652
|
+
annotationMessageMaxBytes: number;
|
|
653
|
+
/** Variables the runner sets: API URL, owner/name, commit and the event payload file. */
|
|
654
|
+
variables: {
|
|
655
|
+
apiUrl: string;
|
|
656
|
+
repository: string;
|
|
657
|
+
sha: string;
|
|
658
|
+
eventPath: string;
|
|
659
|
+
};
|
|
660
|
+
/** Path in the event payload of the commit a pull request check belongs to (GITHUB_SHA is the merge commit there). */
|
|
661
|
+
pullRequestHeadSha: readonly string[];
|
|
662
|
+
}
|
|
342
663
|
interface StepSummaryKnowledge {
|
|
343
664
|
variable: string;
|
|
344
665
|
maxBytes: number;
|
|
@@ -370,12 +691,153 @@ interface GitIgnoreCaseKnowledge {
|
|
|
370
691
|
/** Values read as true (case-insensitive); a key without a value is true too. */
|
|
371
692
|
trueValues: readonly string[];
|
|
372
693
|
}
|
|
694
|
+
/** The file in which Corepack records the version of each package manager it runs outside a project. */
|
|
695
|
+
interface CorepackDefaultsKnowledge {
|
|
696
|
+
/** The first Corepack version with this layout. */
|
|
697
|
+
since: string;
|
|
698
|
+
/** Where the file is: the first directory that applies is used. */
|
|
699
|
+
locations: readonly UserLocation[];
|
|
700
|
+
}
|
|
701
|
+
/** The version of a tool each release of a runtime bundles, for one release line (Node.js 22, CPython 3.12). */
|
|
702
|
+
interface BundledVersionsLine {
|
|
703
|
+
/** The release line: 22 for Node.js, 3.12 for CPython. */
|
|
704
|
+
line: string;
|
|
705
|
+
/** The newest release of the line on the day the fact was verified. */
|
|
706
|
+
latest: string;
|
|
707
|
+
/** [first release, bundled version]: the first release of the line that bundles each version, oldest first. */
|
|
708
|
+
bundles: readonly (readonly [string, string])[];
|
|
709
|
+
}
|
|
710
|
+
/** One Node.js release line in the release schedule. */
|
|
711
|
+
interface ReleaseLineSchedule {
|
|
712
|
+
line: number;
|
|
713
|
+
/** The first release (YYYY-MM-DD). */
|
|
714
|
+
start: string;
|
|
715
|
+
/** When the line became LTS; absent for lines that never do. */
|
|
716
|
+
lts?: string;
|
|
717
|
+
/** End of life: no more releases, security fixes included. */
|
|
718
|
+
end: string;
|
|
719
|
+
/** The LTS codename in lower case, as nvm, setup-node and the node image tags write it. */
|
|
720
|
+
codename?: string;
|
|
721
|
+
}
|
|
722
|
+
/** Names of moving Node.js versions in a version file or a setup-node input. */
|
|
723
|
+
interface NodeVersionNamesKnowledge {
|
|
724
|
+
/** Names of the newest release. */
|
|
725
|
+
newest: readonly string[];
|
|
726
|
+
/** The name of the newest LTS line. */
|
|
727
|
+
newestLts: string;
|
|
728
|
+
/** Followed by a codename: that LTS line (lts/jod). */
|
|
729
|
+
ltsPrefix: string;
|
|
730
|
+
/** Followed by a number n: the n-th LTS line before the newest one (lts/-1); absent when not supported. */
|
|
731
|
+
previousLtsPrefix?: string;
|
|
732
|
+
}
|
|
733
|
+
interface NvmrcKnowledge extends NodeVersionNamesKnowledge {
|
|
734
|
+
file: string;
|
|
735
|
+
/** Starts a comment that runs to the end of the line. */
|
|
736
|
+
comment: string;
|
|
737
|
+
}
|
|
738
|
+
/** How setup-node reads the file named by node-version-file. */
|
|
739
|
+
interface SetupNodeVersionFileKnowledge {
|
|
740
|
+
/** A JSON manifest whose keys are read in order: dotted paths, devEngines.runtime means its entry named node. */
|
|
741
|
+
packageJson: string;
|
|
742
|
+
packageJsonOrder: readonly string[];
|
|
743
|
+
/** Any other file: the first line that matches (multiline), group "version"; otherwise the whole text, trimmed. */
|
|
744
|
+
textPattern: string;
|
|
745
|
+
}
|
|
746
|
+
/** How an image reference names the registry, the namespace and the tag when they are left out. */
|
|
747
|
+
interface ImageReferenceKnowledge {
|
|
748
|
+
defaultHost: string;
|
|
749
|
+
/** Hosts that mean the default registry. */
|
|
750
|
+
hosts: readonly string[];
|
|
751
|
+
/** The namespace of the official images, implied when a Docker Hub name has no namespace. */
|
|
752
|
+
officialNamespace: string;
|
|
753
|
+
defaultTag: string;
|
|
754
|
+
}
|
|
755
|
+
/** The Dockerfile syntax the reader relies on. */
|
|
756
|
+
interface DockerfileSyntaxKnowledge {
|
|
757
|
+
/** Starts a comment line, and a parser directive at the top of the file. */
|
|
758
|
+
comment: string;
|
|
759
|
+
/** The parser directive that sets the escape character, and the characters it accepts (the default first). */
|
|
760
|
+
escapeDirective: string;
|
|
761
|
+
escapeCharacters: readonly string[];
|
|
762
|
+
/** The only instruction allowed before the first FROM. */
|
|
763
|
+
globalArgInstruction: string;
|
|
764
|
+
}
|
|
765
|
+
/** An official runtime image: which tool version it ships and how its tags are built. */
|
|
766
|
+
interface RuntimeImageKnowledge {
|
|
767
|
+
/** The repository name on Docker Hub, in the official namespace. */
|
|
768
|
+
image: string;
|
|
769
|
+
/** The tool the image ships as the runtime release bundles it, unchanged. */
|
|
770
|
+
bundles: 'npm' | 'pip';
|
|
771
|
+
/** Variant names a tag may carry after the version (22-alpine, 3.12-slim-bookworm), or on its own. */
|
|
772
|
+
variants: readonly string[];
|
|
773
|
+
}
|
|
774
|
+
/** Tags of an official image that move to another release line: tag (without variant) to release line. */
|
|
775
|
+
interface MovingTagsKnowledge {
|
|
776
|
+
tags: Readonly<Record<string, string>>;
|
|
777
|
+
}
|
|
373
778
|
interface EnvironmentKnowledge {
|
|
374
779
|
corepack: {
|
|
375
780
|
packageManagerField: Fact<PackageManagerFieldKnowledge>;
|
|
376
781
|
devEnginesSince: Fact<string>;
|
|
377
782
|
envFileSince: Fact<string>;
|
|
378
783
|
bundledWithNode: Fact<Versioned<boolean>>;
|
|
784
|
+
/** The npm package of Corepack, whose package.json declares the shims of the package managers (yarn, pnpm). */
|
|
785
|
+
package: Fact<string>;
|
|
786
|
+
/** The npm packages that install a package manager itself, for the tools Corepack knows. */
|
|
787
|
+
toolPackages: Fact<Readonly<Partial<Record<ToolId, readonly string[]>>>>;
|
|
788
|
+
/** lastKnownGood.json: the version of each package manager Corepack runs outside a project. */
|
|
789
|
+
defaults: Fact<CorepackDefaultsKnowledge>;
|
|
790
|
+
/** corepack enable installs an npm shim only when npm is named. */
|
|
791
|
+
enableNpm: Fact<{
|
|
792
|
+
command: string;
|
|
793
|
+
npmOnlyWhenNamed: boolean;
|
|
794
|
+
}>;
|
|
795
|
+
};
|
|
796
|
+
node: {
|
|
797
|
+
/** The npm each Node.js release bundles, per release line. */
|
|
798
|
+
bundledNpm: Fact<readonly BundledVersionsLine[]>;
|
|
799
|
+
releaseSchedule: Fact<readonly ReleaseLineSchedule[]>;
|
|
800
|
+
nvmrc: Fact<NvmrcKnowledge>;
|
|
801
|
+
/** The version file of nodenv, also read by fnm and setup-node. */
|
|
802
|
+
nodeVersionFile: Fact<{
|
|
803
|
+
file: string;
|
|
804
|
+
}>;
|
|
805
|
+
/** engines.node in package.json: the Node.js versions a package works with, advisory. */
|
|
806
|
+
engines: Fact<{
|
|
807
|
+
key: string;
|
|
808
|
+
runtimeKey: string;
|
|
809
|
+
}>;
|
|
810
|
+
/** The Node.js version files that version managers also use in subdirectories of their directory. */
|
|
811
|
+
inheritedVersionFiles: Fact<readonly string[]>;
|
|
812
|
+
};
|
|
813
|
+
docker: {
|
|
814
|
+
imageReference: Fact<ImageReferenceKnowledge>;
|
|
815
|
+
syntax: Fact<DockerfileSyntaxKnowledge>;
|
|
816
|
+
/** How COPY and ADD read a source path in the build context: from its root, with ../ and trailing slashes removed. */
|
|
817
|
+
copySources: Fact<{
|
|
818
|
+
relativeTo: 'context';
|
|
819
|
+
parentNavigation: 'removed';
|
|
820
|
+
trailingSlash: 'ignored';
|
|
821
|
+
}>;
|
|
822
|
+
/** ENV: variables for the later instructions of the stage and the stages that start from it (decision D-115). */
|
|
823
|
+
environment: Fact<{
|
|
824
|
+
instruction: string;
|
|
825
|
+
inheritedByStages: boolean;
|
|
826
|
+
spaceForm: boolean;
|
|
827
|
+
}>;
|
|
828
|
+
/** The default file name, and the suffix of other Dockerfiles (build.Dockerfile). */
|
|
829
|
+
dockerfileNames: Fact<{
|
|
830
|
+
defaultName: string;
|
|
831
|
+
suffix: string;
|
|
832
|
+
}>;
|
|
833
|
+
/** Dockerfile.<name>: a common convention without an official source. */
|
|
834
|
+
dockerfilePrefix: Fact<string>;
|
|
835
|
+
/** The file name Buildah and Podman read by default. */
|
|
836
|
+
containerfile: Fact<string>;
|
|
837
|
+
nodeImage: Fact<RuntimeImageKnowledge>;
|
|
838
|
+
nodeImageTags: Fact<MovingTagsKnowledge>;
|
|
839
|
+
pythonImage: Fact<RuntimeImageKnowledge>;
|
|
840
|
+
pythonImageTags: Fact<MovingTagsKnowledge>;
|
|
379
841
|
};
|
|
380
842
|
devEngines: Fact<DevEnginesKnowledge>;
|
|
381
843
|
devEnginesSince: Fact<string>;
|
|
@@ -392,6 +854,23 @@ interface EnvironmentKnowledge {
|
|
|
392
854
|
githubActions: {
|
|
393
855
|
workflows: Fact<WorkflowsKnowledge>;
|
|
394
856
|
setupNode: Fact<SetupActionKnowledge>;
|
|
857
|
+
/** The versions node-version accepts besides semver ranges. */
|
|
858
|
+
setupNodeVersions: Fact<NodeVersionNamesKnowledge>;
|
|
859
|
+
setupNodeVersionFile: Fact<SetupNodeVersionFileKnowledge>;
|
|
860
|
+
/**
|
|
861
|
+
* jobs.<job_id>.container: the image the job's run steps run in, as a string or under image, with its env
|
|
862
|
+
* (decision D-115).
|
|
863
|
+
*/
|
|
864
|
+
jobContainer: Fact<{
|
|
865
|
+
key: string;
|
|
866
|
+
imageKey: string;
|
|
867
|
+
envKey: string;
|
|
868
|
+
}>;
|
|
869
|
+
/** The env maps of a workflow, a job and a step; the most specific one wins (decision D-115). */
|
|
870
|
+
environmentKeys: Fact<{
|
|
871
|
+
key: string;
|
|
872
|
+
levels: readonly ('workflow' | 'job' | 'step')[];
|
|
873
|
+
}>;
|
|
395
874
|
pnpmActionSetup: Fact<SetupActionKnowledge>;
|
|
396
875
|
pnpmSetup: Fact<SetupActionKnowledge>;
|
|
397
876
|
setupBun: Fact<SetupActionKnowledge>;
|
|
@@ -406,6 +885,7 @@ interface EnvironmentKnowledge {
|
|
|
406
885
|
annotations: Fact<AnnotationsKnowledge>;
|
|
407
886
|
annotationLimits: Fact<AnnotationLimitsKnowledge>;
|
|
408
887
|
stepSummary: Fact<StepSummaryKnowledge>;
|
|
888
|
+
checkRuns: Fact<CheckRunsKnowledge>;
|
|
409
889
|
};
|
|
410
890
|
npmRegistry: {
|
|
411
891
|
api: Fact<NpmRegistryKnowledge>;
|
|
@@ -425,11 +905,36 @@ interface EnvironmentKnowledge {
|
|
|
425
905
|
hooks: Fact<ClaudeCodeHooksKnowledge>;
|
|
426
906
|
/** The tools whose PreToolUse events carry a shell command in the hooks fact's input.command. */
|
|
427
907
|
shellTools: Fact<readonly ClaudeCodeShellTool[]>;
|
|
908
|
+
sessionStart: Fact<ClaudeCodeSessionStartKnowledge>;
|
|
909
|
+
/** The field of a PreToolUse hook's output that adds text to Claude's context, with or without a decision. */
|
|
910
|
+
hookContext: Fact<ClaudeCodeHookContextKnowledge>;
|
|
911
|
+
/** Shell tool calls without a command: a Monitor WebSocket watch. */
|
|
912
|
+
commandlessInputs: Fact<readonly ClaudeCodeCommandlessInputKnowledge[]>;
|
|
913
|
+
};
|
|
914
|
+
/** The Model Context Protocol, for depcurfew mcp, and how its clients start a stdio server. */
|
|
915
|
+
mcp: {
|
|
916
|
+
revisions: Fact<McpRevisionsKnowledge>;
|
|
917
|
+
lifecycle: Fact<McpLifecycleKnowledge>;
|
|
918
|
+
stateless: Fact<McpStatelessKnowledge>;
|
|
919
|
+
tools: Fact<McpToolsKnowledge>;
|
|
920
|
+
cancellation: Fact<McpCancellationKnowledge>;
|
|
921
|
+
errorCodes: Fact<McpErrorCodesKnowledge>;
|
|
922
|
+
errorId: Fact<McpErrorIdKnowledge>;
|
|
923
|
+
stdio: Fact<McpStdioKnowledge>;
|
|
924
|
+
clients: McpClientsKnowledge;
|
|
428
925
|
};
|
|
429
926
|
git: {
|
|
430
927
|
/** Where git reads the patterns of files it does not pick up, and the index that lists the tracked files. */
|
|
431
928
|
ignore: Fact<GitIgnoreKnowledge>;
|
|
432
929
|
ignoreCase: Fact<GitIgnoreCaseKnowledge>;
|
|
930
|
+
/**
|
|
931
|
+
* The entry at the root of a working tree that makes a directory a repository of its own: a .git directory, or in
|
|
932
|
+
* a submodule a .git file pointing to its git directory in the superproject (decision D-115).
|
|
933
|
+
*/
|
|
934
|
+
repositoryEntry: Fact<{
|
|
935
|
+
entry: string;
|
|
936
|
+
submoduleEntry: 'file';
|
|
937
|
+
}>;
|
|
433
938
|
};
|
|
434
939
|
windows: {
|
|
435
940
|
commandShims: Fact<CommandShimsKnowledge>;
|
|
@@ -453,6 +958,10 @@ interface EnvironmentKnowledge {
|
|
|
453
958
|
/** File extensions of source distributions on an index, which are built when installed. */
|
|
454
959
|
sourceDistributionExtensions: Fact<readonly string[]>;
|
|
455
960
|
interpreter: Fact<PythonInterpreterKnowledge>;
|
|
961
|
+
/** A virtual environment made by venv: the file in its directory that marks it, and its activation scripts. */
|
|
962
|
+
virtualEnvironment: Fact<VirtualEnvironmentKnowledge>;
|
|
963
|
+
/** The pip each CPython release bundles in ensurepip, per release line. */
|
|
964
|
+
bundledPip: Fact<readonly BundledVersionsLine[]>;
|
|
456
965
|
/** Hatch, which has no supply-chain settings of its own: how a project shows it uses Hatch. */
|
|
457
966
|
hatch: Fact<{
|
|
458
967
|
pyprojectTable: string;
|
|
@@ -469,18 +978,125 @@ interface Evidence {
|
|
|
469
978
|
/** Repository-relative path with `/` separators. */
|
|
470
979
|
file: string;
|
|
471
980
|
}
|
|
472
|
-
/**
|
|
473
|
-
|
|
474
|
-
|
|
981
|
+
/**
|
|
982
|
+
* tool-config: a version the tool's own configuration requires, e.g. required-version in [tool.uv]. nvmrc and
|
|
983
|
+
* node-version: the files .nvmrc and .node-version; engines: engines.node in package.json (a range of the Node.js
|
|
984
|
+
* versions the package works on, advisory); dockerfile: a Dockerfile (FROM node:22, RUN npm install -g npm@11).
|
|
985
|
+
*/
|
|
986
|
+
type VersionSource = 'packageManager' | 'tool-config' | 'devEngines' | 'volta' | 'tool-versions' | 'nvmrc' | 'node-version' | 'engines' | 'github-actions' | 'dockerfile';
|
|
987
|
+
/**
|
|
988
|
+
* A version declared somewhere in the repository, as written (it may be a range or an expression). A CI step that
|
|
989
|
+
* names a version file has `versionFile`, and also `version` when depcurfew could read the version from that file.
|
|
990
|
+
*/
|
|
475
991
|
type VersionDeclaration = ({
|
|
476
992
|
version: string;
|
|
993
|
+
versionFile?: string;
|
|
477
994
|
} | {
|
|
478
995
|
versionFile: string;
|
|
479
996
|
}) & {
|
|
480
997
|
source: VersionSource;
|
|
481
998
|
file: string;
|
|
482
999
|
line?: number;
|
|
1000
|
+
/** github-actions: the key of the job under jobs. */
|
|
1001
|
+
job?: string;
|
|
1002
|
+
/** github-actions: the line of the step that sets the version up; installs after it and before the next such step use it. */
|
|
1003
|
+
step?: number;
|
|
1004
|
+
/** github-actions: the version is one value of this matrix key, from ${{ matrix.<key> }}; the line is the value's. */
|
|
1005
|
+
matrix?: string;
|
|
1006
|
+
/** Why depcurfew does not read the version written (a matrix with too many values); the version is then as written. */
|
|
1007
|
+
unknown?: string;
|
|
1008
|
+
/** dockerfile: the build stage, by its name or its number from 0. */
|
|
1009
|
+
stage?: string;
|
|
1010
|
+
/**
|
|
1011
|
+
* github-actions: the image of jobs.<id>.container (node:20) the version comes from, read like a FROM of a Dockerfile
|
|
1012
|
+
* (decision D-115); the line is the image's, and it stands for the job's steps until a setup step.
|
|
1013
|
+
*/
|
|
1014
|
+
container?: string;
|
|
483
1015
|
};
|
|
1016
|
+
/**
|
|
1017
|
+
* A command in a CI run step or a Dockerfile RUN instruction. install: installs packages with the tool (npm ci,
|
|
1018
|
+
* npm install, pip install -r requirements.txt, pnpm install, yarn, bun add); upgrade: replaces the tool itself
|
|
1019
|
+
* (npm install -g npm@11.17.0, pip install --upgrade pip, corepack enable npm). npm install -g with another package is
|
|
1020
|
+
* neither: npm does not read the project .npmrc in global mode.
|
|
1021
|
+
*/
|
|
1022
|
+
interface ToolCommand {
|
|
1023
|
+
kind: 'install' | 'upgrade';
|
|
1024
|
+
/**
|
|
1025
|
+
* pnpm, Yarn and Bun only install (their versions come from packageManager and the setup actions); poetry only
|
|
1026
|
+
* upgrades: pipx install poetry==2.3.0 or pip install poetry==2.3.0 installs that Poetry (decision D-115).
|
|
1027
|
+
*/
|
|
1028
|
+
tool: 'npm' | 'pip' | 'pnpm' | 'yarn' | 'bun' | 'poetry';
|
|
1029
|
+
/** upgrade: the version asked for as written, latest when none is; absent when the command does not say (corepack enable npm). */
|
|
1030
|
+
version?: string;
|
|
1031
|
+
/** npm ci and its aliases, which install exactly the versions of package-lock.json (decision D-115). */
|
|
1032
|
+
lockfile?: true;
|
|
1033
|
+
/** npm runs it with dependency install scripts off (--ignore-scripts, npm_config_ignore_scripts=true; decision D-115). */
|
|
1034
|
+
scriptsOff?: true;
|
|
1035
|
+
/** pip install: the release-age delay it gets, as written (--uploaded-prior-to, PIP_UPLOADED_PRIOR_TO; decision D-115). */
|
|
1036
|
+
delay?: string;
|
|
1037
|
+
/** pip install: PIP_CONFIG_FILE names a file that may set the delay, or the value is a variable depcurfew does not read. */
|
|
1038
|
+
delayUnknown?: true;
|
|
1039
|
+
line: number;
|
|
1040
|
+
/**
|
|
1041
|
+
* github-actions: the working directory of the run step as written (working-directory of the step, or
|
|
1042
|
+
* defaults.run.working-directory of the job or the workflow), without ./ and a trailing /, joined with the literal
|
|
1043
|
+
* cd before the command (cd frontend && npm ci); absent for the root. dockerfile: the directory of a literal cd before
|
|
1044
|
+
* the command, relative to the working directory of the instruction. A cd depcurfew does not follow ($DIR, an
|
|
1045
|
+
* absolute path) gives a directory that is no project (decision D-115).
|
|
1046
|
+
*/
|
|
1047
|
+
directory?: string;
|
|
1048
|
+
}
|
|
1049
|
+
/** A GitHub Actions job, with what its run steps do with npm and pip. */
|
|
1050
|
+
interface WorkflowJob {
|
|
1051
|
+
file: string;
|
|
1052
|
+
/** The key of the job under jobs. */
|
|
1053
|
+
id: string;
|
|
1054
|
+
line: number;
|
|
1055
|
+
/** Installs and package manager changes, in order. */
|
|
1056
|
+
commands: ToolCommand[];
|
|
1057
|
+
/** A run step calls node, npm, npx, pnpm, yarn, bun or corepack, so the job uses the Node.js it sets up. */
|
|
1058
|
+
runsNode: boolean;
|
|
1059
|
+
/**
|
|
1060
|
+
* A run step may change pip's configuration (pip config set, a pip.conf it writes, PIP_CONFIG_FILE in env), so the
|
|
1061
|
+
* release-age delay of its pip installs cannot be told (decision D-115).
|
|
1062
|
+
*/
|
|
1063
|
+
pipConfig?: true;
|
|
1064
|
+
}
|
|
1065
|
+
/** The official runtime image a build stage starts from. */
|
|
1066
|
+
interface StageRuntime {
|
|
1067
|
+
tool: 'node' | 'python';
|
|
1068
|
+
/** The tag as written after ARG substitution; absent when the image has none (latest, or a digest alone). */
|
|
1069
|
+
tag?: string;
|
|
1070
|
+
/** The version the tag names: 22, 22.11.0, lts, jod, latest (moving tags stay names); absent when it cannot be told. */
|
|
1071
|
+
version?: string;
|
|
1072
|
+
/** Why the version cannot be told. */
|
|
1073
|
+
unknown?: string;
|
|
1074
|
+
}
|
|
1075
|
+
interface DockerStage {
|
|
1076
|
+
/** The line of its FROM instruction. */
|
|
1077
|
+
line: number;
|
|
1078
|
+
/** The name after AS, in lower case. */
|
|
1079
|
+
name?: string;
|
|
1080
|
+
/** The image after ARG substitution; absent when an ARG it uses has no value. */
|
|
1081
|
+
image?: string;
|
|
1082
|
+
/** The earlier stage this one starts from (FROM <name>), by its number from 0. */
|
|
1083
|
+
from?: number;
|
|
1084
|
+
/** The official node or python image of this stage or of the stage it starts from. */
|
|
1085
|
+
runtime?: StageRuntime;
|
|
1086
|
+
commands: ToolCommand[];
|
|
1087
|
+
/** A RUN may change pip's configuration (pip config set, a pip.conf it writes), as for jobs (decision D-115). */
|
|
1088
|
+
pipConfig?: true;
|
|
1089
|
+
/**
|
|
1090
|
+
* The source paths of the stage's COPY and ADD instructions that copy from the build context (not --from), as
|
|
1091
|
+
* written; absent when there are none. A root Dockerfile whose stage copies only files of one directory listed in
|
|
1092
|
+
* projects builds that project (decision D-109). At most 256.
|
|
1093
|
+
*/
|
|
1094
|
+
copies?: string[];
|
|
1095
|
+
}
|
|
1096
|
+
interface DockerfileInfo {
|
|
1097
|
+
file: string;
|
|
1098
|
+
stages: DockerStage[];
|
|
1099
|
+
}
|
|
484
1100
|
interface PackageManagerInfo {
|
|
485
1101
|
id: PackageManagerId;
|
|
486
1102
|
/** Yarn only: Berry (2+) or Classic (1.x). */
|
|
@@ -508,6 +1124,8 @@ interface DetectionWarning {
|
|
|
508
1124
|
code: string;
|
|
509
1125
|
message: string;
|
|
510
1126
|
files: string[];
|
|
1127
|
+
/** The line in the first file the warning is about, when it names one (decision D-124). */
|
|
1128
|
+
line?: number;
|
|
511
1129
|
}
|
|
512
1130
|
interface Detection {
|
|
513
1131
|
packageManagers: PackageManagerInfo[];
|
|
@@ -520,8 +1138,98 @@ interface Detection {
|
|
|
520
1138
|
npmrcFiles: string[];
|
|
521
1139
|
/** GitHub Actions workflow files that were read. */
|
|
522
1140
|
workflows: string[];
|
|
1141
|
+
/** The jobs of those workflows. */
|
|
1142
|
+
jobs: WorkflowJob[];
|
|
1143
|
+
/** Dockerfiles and Containerfiles of the root, its direct subdirectories without a manifest, and workspace packages. */
|
|
1144
|
+
dockerfiles: DockerfileInfo[];
|
|
523
1145
|
warnings: DetectionWarning[];
|
|
524
1146
|
}
|
|
1147
|
+
interface UserEnvironment {
|
|
1148
|
+
platform: NodeJS.Platform;
|
|
1149
|
+
/** HOME on Linux and macOS, USERPROFILE on Windows: what os.homedir() returns. */
|
|
1150
|
+
home: string;
|
|
1151
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
1152
|
+
/** The current directory, against which a tool resolves a relative XDG_CONFIG_HOME (relativeToCwd); process.cwd() by default. */
|
|
1153
|
+
cwd?: string;
|
|
1154
|
+
/** Whether a directory exists; replaceable in tests. */
|
|
1155
|
+
isDirectory?: (path: string) => boolean;
|
|
1156
|
+
}
|
|
1157
|
+
type FetchLike = (url: string, init: {
|
|
1158
|
+
headers: Record<string, string>;
|
|
1159
|
+
signal: AbortSignal;
|
|
1160
|
+
redirect?: 'manual';
|
|
1161
|
+
}) => Promise<Response>;
|
|
1162
|
+
/**
|
|
1163
|
+
* Lookups shared by the checks of one run, by registry or index and name: a command checked against two policies
|
|
1164
|
+
* fetches and reads each document once (decision D-113).
|
|
1165
|
+
*/
|
|
1166
|
+
type LookupCache = Map<string, Promise<unknown>>;
|
|
1167
|
+
interface McpLimits {
|
|
1168
|
+
/** The longest line, in bytes; a longer one is answered with an error and skipped. */
|
|
1169
|
+
lineBytes: number;
|
|
1170
|
+
/** The deepest nesting of arrays and objects in a message. */
|
|
1171
|
+
depth: number;
|
|
1172
|
+
/** Tool calls that run at once. */
|
|
1173
|
+
concurrentCalls: number;
|
|
1174
|
+
/** Tool calls that wait for one of them; a call beyond that gets a result that says to try again. */
|
|
1175
|
+
queuedCalls: number;
|
|
1176
|
+
/** Messages in one batch (2025-03-26 only). */
|
|
1177
|
+
batch: number;
|
|
1178
|
+
/** How long calls may still run after stdin ends before they are stopped. */
|
|
1179
|
+
graceMs: number;
|
|
1180
|
+
}
|
|
1181
|
+
declare const EXIT_OK = 0;
|
|
1182
|
+
declare const EXIT_FAILURE = 1;
|
|
1183
|
+
declare const EXIT_USAGE = 2;
|
|
1184
|
+
interface CliIo {
|
|
1185
|
+
stdout(text: string): void;
|
|
1186
|
+
stderr(text: string): void;
|
|
1187
|
+
/** Whether each stream may be colored (see color.ts); absent means plain text. */
|
|
1188
|
+
colors?: {
|
|
1189
|
+
stdout: boolean;
|
|
1190
|
+
stderr: boolean;
|
|
1191
|
+
};
|
|
1192
|
+
/** Whether each stream is a terminal; absent means it is not. */
|
|
1193
|
+
terminal?: {
|
|
1194
|
+
stdout: boolean;
|
|
1195
|
+
stderr: boolean;
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
/** What the server talks over and the process around it; tests replace it. */
|
|
1199
|
+
interface McpProcess {
|
|
1200
|
+
input: Readable;
|
|
1201
|
+
output: Writable;
|
|
1202
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
1203
|
+
cwd: string;
|
|
1204
|
+
/** Calls `stop` on SIGINT and SIGTERM; returns what removes the handlers. Left out in tests. */
|
|
1205
|
+
onSignal?: (stop: () => void) => () => void;
|
|
1206
|
+
limits?: Partial<McpLimits>;
|
|
1207
|
+
}
|
|
1208
|
+
declare class Repository {
|
|
1209
|
+
#private;
|
|
1210
|
+
readonly root: string;
|
|
1211
|
+
readonly warnings: DetectionWarning[];
|
|
1212
|
+
/** Repository-relative paths of the files that were read, for the log of what doctor checked. */
|
|
1213
|
+
readonly read: Set<string>;
|
|
1214
|
+
constructor(root: string);
|
|
1215
|
+
warn(code: string, message: string, files: readonly string[], line?: number): void;
|
|
1216
|
+
isFile(path: string): boolean;
|
|
1217
|
+
/** Whether the path exists in the repository as something other than a regular file or a directory (a named pipe, a socket, a device). */
|
|
1218
|
+
isSpecialFile(path: string): boolean;
|
|
1219
|
+
/** The text of a file, without a byte order mark; undefined (with a warning when useful) if it cannot be read. */
|
|
1220
|
+
readText(path: string, limit?: number): string | undefined;
|
|
1221
|
+
/** The first bytes of a file, e.g. to recognize a lockfile format without reading a large file. */
|
|
1222
|
+
readHead(path: string, bytes?: number): string | undefined;
|
|
1223
|
+
/** Names of the files in a directory (following symlinks only inside the repository). */
|
|
1224
|
+
listFiles(path: string): string[];
|
|
1225
|
+
/** Names of the subdirectories of a directory, skipping node_modules, .git and symlinks that lead outside. */
|
|
1226
|
+
listDirectories(path: string): string[];
|
|
1227
|
+
/** Parses a file with the given parser, reporting failures as warnings. */
|
|
1228
|
+
parse<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1229
|
+
/** Like parse, but each file is read and parsed once per parser: several rules read the same file. */
|
|
1230
|
+
parseOnce<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
1231
|
+
relative(absolute: string): string;
|
|
1232
|
+
}
|
|
525
1233
|
interface Duration {
|
|
526
1234
|
/** The duration as written in the policy. */
|
|
527
1235
|
text: string;
|
|
@@ -581,6 +1289,8 @@ interface Shape<In, Out = In> {
|
|
|
581
1289
|
}
|
|
582
1290
|
type Input<S> = S extends Shape<infer In, unknown> ? In : never;
|
|
583
1291
|
type Output<S> = S extends Shape<unknown, infer Out> ? Out : never;
|
|
1292
|
+
/** Which findings make depcurfew doctor exit with 1 (decision D-112). */
|
|
1293
|
+
declare const FAIL_ON_LEVELS: readonly ["error", "warning"];
|
|
584
1294
|
declare const policyShape: Shape<{
|
|
585
1295
|
version: 1;
|
|
586
1296
|
$schema?: string;
|
|
@@ -592,6 +1302,7 @@ declare const policyShape: Shape<{
|
|
|
592
1302
|
installScripts?: {
|
|
593
1303
|
default: "allow" | "deny";
|
|
594
1304
|
allow?: readonly string[];
|
|
1305
|
+
deny?: readonly string[];
|
|
595
1306
|
};
|
|
596
1307
|
sources?: {
|
|
597
1308
|
git?: "allow" | "deny";
|
|
@@ -600,9 +1311,14 @@ declare const policyShape: Shape<{
|
|
|
600
1311
|
directory?: "allow" | "deny";
|
|
601
1312
|
};
|
|
602
1313
|
targets?: "auto" | readonly ToolId[];
|
|
1314
|
+
projects?: readonly string[];
|
|
1315
|
+
doctor?: {
|
|
1316
|
+
failOn?: "error" | "warning";
|
|
1317
|
+
};
|
|
603
1318
|
}, {
|
|
604
1319
|
version: 1;
|
|
605
1320
|
targets: "auto" | readonly ToolId[];
|
|
1321
|
+
projects: readonly string[];
|
|
606
1322
|
$schema?: string;
|
|
607
1323
|
cooldown?: {
|
|
608
1324
|
minimum: Duration;
|
|
@@ -612,6 +1328,7 @@ declare const policyShape: Shape<{
|
|
|
612
1328
|
installScripts?: {
|
|
613
1329
|
default: "allow" | "deny";
|
|
614
1330
|
allow: readonly string[];
|
|
1331
|
+
deny: readonly string[];
|
|
615
1332
|
};
|
|
616
1333
|
sources?: {
|
|
617
1334
|
git?: "allow" | "deny";
|
|
@@ -619,31 +1336,205 @@ declare const policyShape: Shape<{
|
|
|
619
1336
|
file?: "allow" | "deny";
|
|
620
1337
|
directory?: "allow" | "deny";
|
|
621
1338
|
};
|
|
1339
|
+
doctor?: {
|
|
1340
|
+
failOn?: "error" | "warning";
|
|
1341
|
+
};
|
|
622
1342
|
}>;
|
|
623
1343
|
/** The policy as written in depcurfew.json. */
|
|
624
1344
|
type Policy = Input<typeof policyShape>;
|
|
625
1345
|
/** The policy after validation: defaults filled in, durations parsed. */
|
|
626
1346
|
type ResolvedPolicy = Output<typeof policyShape>;
|
|
1347
|
+
/** The SARIF levels: an error fails `doctor`, warnings and notes do not. */
|
|
1348
|
+
type Level = 'error' | 'warning' | 'note';
|
|
1349
|
+
interface Location {
|
|
1350
|
+
/** Repository-relative path with `/` separators. */
|
|
1351
|
+
file: string;
|
|
1352
|
+
/** 1-based; absent when the finding concerns the whole file. */
|
|
1353
|
+
line?: number;
|
|
1354
|
+
/** 1-based column on `line`, when the problem has one (a syntax error); absent otherwise (decision D-116). */
|
|
1355
|
+
column?: number;
|
|
1356
|
+
/** doctor --user: the environment variable, as it is set, of a finding whose file is "environment". */
|
|
1357
|
+
variable?: string;
|
|
1358
|
+
}
|
|
1359
|
+
interface Finding {
|
|
1360
|
+
rule: string;
|
|
1361
|
+
level: Level;
|
|
1362
|
+
message: string;
|
|
1363
|
+
location: Location;
|
|
1364
|
+
/** A fix for this finding; the rule's general help applies when absent. */
|
|
1365
|
+
fix?: string;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Something doctor could not check, such as an unreadable file. Not a problem in the repository itself, so it never
|
|
1369
|
+
* fails doctor. It has the fields of a finding (decision D-116): the level, always note; a code, the rule that could not
|
|
1370
|
+
* check something or the code of the warning or state that gave it; where it is; the message; and what to do.
|
|
1371
|
+
*/
|
|
1372
|
+
interface Notice {
|
|
1373
|
+
level: 'note';
|
|
1374
|
+
code: string;
|
|
1375
|
+
message: string;
|
|
1376
|
+
/** The file it concerns, the same as location.file; kept for programs that read it before location was added. */
|
|
1377
|
+
file?: string;
|
|
1378
|
+
/** The line, when the notice is about one (a CI run step or RUN instruction depcurfew did not follow, decision D-124). */
|
|
1379
|
+
location?: {
|
|
1380
|
+
file: string;
|
|
1381
|
+
line?: number;
|
|
1382
|
+
};
|
|
1383
|
+
fix?: string;
|
|
1384
|
+
}
|
|
1385
|
+
interface RuleContext {
|
|
1386
|
+
root: string;
|
|
1387
|
+
repository: Repository;
|
|
1388
|
+
detection: Detection;
|
|
1389
|
+
/** undefined when the repository has no depcurfew.json. */
|
|
1390
|
+
policy: ResolvedPolicy | undefined;
|
|
1391
|
+
tools: readonly ToolKnowledge[];
|
|
1392
|
+
report(finding: Omit<Finding, 'rule' | 'level'> & {
|
|
1393
|
+
level?: Level;
|
|
1394
|
+
}): void;
|
|
1395
|
+
/** Something the rule could not check; its code is the rule's id (decision D-116). */
|
|
1396
|
+
notice(message: string, file?: string, fix?: string): void;
|
|
1397
|
+
/**
|
|
1398
|
+
* Set when the rules run on a directory listed in projects in depcurfew.json (decision D-100): its path relative to
|
|
1399
|
+
* the repository root. `root`, `repository` and `detection` are then the project's, and file names relative to it.
|
|
1400
|
+
*/
|
|
1401
|
+
project?: string;
|
|
1402
|
+
/**
|
|
1403
|
+
* At the repository root: the directories listed in projects that could be read, for the rules that compare the
|
|
1404
|
+
* bots, which are configured from the root, with the package managers of every project.
|
|
1405
|
+
*/
|
|
1406
|
+
projects?: readonly ProjectView[];
|
|
1407
|
+
/** How apply plans this directory, for the rules that compare with that plan (config-drift). */
|
|
1408
|
+
plan?: PlanOptions;
|
|
1409
|
+
}
|
|
1410
|
+
/** A directory listed in projects, as the rules at the repository root see it. */
|
|
1411
|
+
interface ProjectView {
|
|
1412
|
+
/** Relative to the repository root, as listed. */
|
|
1413
|
+
directory: string;
|
|
1414
|
+
/** The absolute path. */
|
|
1415
|
+
root: string;
|
|
1416
|
+
repository: Repository;
|
|
1417
|
+
detection: Detection;
|
|
1418
|
+
}
|
|
1419
|
+
/** What a rule may look at to decide whether it concerns the project: the context without the reporting functions. */
|
|
1420
|
+
type RuleScope = Omit<RuleContext, 'report' | 'notice'>;
|
|
1421
|
+
interface Rule {
|
|
1422
|
+
/** Stable kebab-case identifier, used in SARIF and in the documentation. */
|
|
1423
|
+
id: string;
|
|
1424
|
+
/** One sentence: what the rule finds. */
|
|
1425
|
+
summary: string;
|
|
1426
|
+
/** What the rule checks, said as what holds when it passes, for the list of what doctor checked. */
|
|
1427
|
+
checks?: string;
|
|
1428
|
+
/** Why it matters. */
|
|
1429
|
+
description: string;
|
|
1430
|
+
/** How to fix it in general. */
|
|
1431
|
+
help: string;
|
|
1432
|
+
/** Default level of its findings. */
|
|
1433
|
+
level: Level;
|
|
1434
|
+
/** Rules that compare the repository with depcurfew.json do not run without one. */
|
|
1435
|
+
requiresPolicy: boolean;
|
|
1436
|
+
/**
|
|
1437
|
+
* Whether the rule concerns the project at all, from detection: the Python rules without a Python tool, the bot rules
|
|
1438
|
+
* without a bot. false shows the rule as "not applicable" in the list of what doctor checked (decision D-098). It
|
|
1439
|
+
* never changes findings: check still runs, and a rule that reports one has problems. Absent: it always applies.
|
|
1440
|
+
*/
|
|
1441
|
+
applies?(scope: RuleScope): boolean;
|
|
1442
|
+
check(context: RuleContext): void;
|
|
1443
|
+
}
|
|
1444
|
+
/** A package manager or bot doctor checked, with the version it checked against when one is known. */
|
|
1445
|
+
interface CheckedTool {
|
|
1446
|
+
id: string;
|
|
1447
|
+
name: string;
|
|
1448
|
+
/** The directory listed in projects the tool was found in; absent for the repository root. */
|
|
1449
|
+
project?: string;
|
|
1450
|
+
version?: string;
|
|
1451
|
+
/** Where the version comes from: a repository file, or PATH for doctor --user. */
|
|
1452
|
+
versionFrom?: string;
|
|
1453
|
+
/** A bot configuration in a directory listed in projects, which the bot does not read (it reads the root's, decision D-109). */
|
|
1454
|
+
unread?: true;
|
|
1455
|
+
}
|
|
1456
|
+
/** One check of the list of what doctor checked: a rule and what came of it. */
|
|
1457
|
+
interface RuleResult {
|
|
1458
|
+
rule: string;
|
|
1459
|
+
/** What the rule checks, in plain words (Rule.check). */
|
|
1460
|
+
check: string;
|
|
1461
|
+
/**
|
|
1462
|
+
* passed: it ran and found nothing; problems: it reported findings; skipped: it needs a policy that is missing or
|
|
1463
|
+
* invalid; not-applicable: it does not concern the project, e.g. a Python rule without a Python tool (decision D-098).
|
|
1464
|
+
*/
|
|
1465
|
+
result: 'passed' | 'problems' | 'skipped' | 'not-applicable';
|
|
1466
|
+
errors: number;
|
|
1467
|
+
warnings: number;
|
|
1468
|
+
notes: number;
|
|
1469
|
+
}
|
|
1470
|
+
/** What doctor looked at, shown below the result, so that a clean report says what it covers. */
|
|
1471
|
+
interface Checked {
|
|
1472
|
+
tools: CheckedTool[];
|
|
1473
|
+
/** Files that were read, repository-relative (doctor) or as shown (~/.npmrc, doctor --user). */
|
|
1474
|
+
files: string[];
|
|
1475
|
+
/** Every rule, in the order it ran, with its result. */
|
|
1476
|
+
rules: RuleResult[];
|
|
1477
|
+
/** The policy that skipped rules need, as messages show it. */
|
|
1478
|
+
policyFile: string;
|
|
1479
|
+
}
|
|
1480
|
+
interface DoctorReport {
|
|
1481
|
+
schemaVersion: 1;
|
|
1482
|
+
/** invalid: depcurfew.json does not follow the schema or cannot be read (only with invalidPolicy: 'report'). */
|
|
1483
|
+
policy: 'valid' | 'missing' | 'invalid';
|
|
1484
|
+
findings: Finding[];
|
|
1485
|
+
notices: Notice[];
|
|
1486
|
+
/** What was checked. */
|
|
1487
|
+
checked?: Checked;
|
|
1488
|
+
}
|
|
1489
|
+
/** What a directory listed in projects takes from the repository root besides its CI installs (decision D-109). */
|
|
1490
|
+
interface RootPlaces {
|
|
1491
|
+
/** The project; the stages of root Dockerfiles are then read for it (decision D-115). */
|
|
1492
|
+
project?: string;
|
|
1493
|
+
/** The root's Node.js version files (.nvmrc, .node-version, .tool-versions) of the kinds the project has none of. */
|
|
1494
|
+
versions: readonly VersionDeclaration[];
|
|
1495
|
+
/**
|
|
1496
|
+
* Root Dockerfiles with the stages that build the project (they copy only its files, decision D-109) or install in its
|
|
1497
|
+
* directory after copying the whole build context (cd frontend && npm ci, decision D-115).
|
|
1498
|
+
*/
|
|
1499
|
+
dockerfiles: readonly {
|
|
1500
|
+
file: DockerfileInfo;
|
|
1501
|
+
stages: readonly number[];
|
|
1502
|
+
}[];
|
|
1503
|
+
}
|
|
627
1504
|
declare const APPLY_WARNINGS: {
|
|
628
1505
|
readonly 'nothing-to-configure': "depcurfew found no supported package manager or bot, so nothing is configured.";
|
|
1506
|
+
readonly 'environment-overrides': "apply --user: an environment variable makes a tool read another user file or none, or overrides a value apply writes there.";
|
|
1507
|
+
readonly 'depcurfew-outdated': "The project installs a newer depcurfew than the one running, which may not know settings that version writes.";
|
|
629
1508
|
readonly 'file-unreadable': "A file that shows which tools a project uses cannot be read or parsed (too large, a symlink out of the repository, no read permission, invalid), so depcurfew may configure fewer tools than the project uses.";
|
|
630
|
-
readonly 'tool-not-supported': "A detected tool is not supported (Yarn Classic), so nothing is written for it.";
|
|
1509
|
+
readonly 'tool-not-supported': "A detected or installed tool is not supported (Yarn Classic), so nothing is written for it.";
|
|
1510
|
+
readonly 'target-not-found': "With projects, a package manager listed under targets is configured in each directory (the root or a project) that uses it, and none uses it, so nothing is written for it.";
|
|
631
1511
|
readonly 'tool-only-in-ci': "A tool appears only in a CI setup step, with no manifest, lockfile or configuration in the repository, so targets \"auto\" does not configure it.";
|
|
632
1512
|
readonly 'version-not-pinned': "No exact version of a package manager is pinned, so depcurfew configures for the latest version it knows.";
|
|
633
|
-
readonly 'version-too-old': "The pinned version of a tool lacks a setting the policy needs, so that setting is not written.";
|
|
1513
|
+
readonly 'version-too-old': "The pinned version of a tool (with apply --user, the installed one) lacks a setting the policy needs, so that setting is not written.";
|
|
1514
|
+
readonly 'version-unknown': "apply --user: the installed version of a tool cannot be read from its files, and a value it does not understand would stop every command, so nothing is written for it.";
|
|
1515
|
+
readonly 'no-user-config': "apply --user: the tool has no user-level configuration file, so nothing is written for it.";
|
|
1516
|
+
readonly 'user-setting-project-only': "apply --user: the tool reads a part of the policy only from project files, so it is not written into the user file.";
|
|
1517
|
+
readonly 'user-directory-relative': "apply --user: the environment variable that names the directory of the user file (XDG_CONFIG_HOME, APPDATA) is a relative path, so the tool looks in another place from each directory, or where it looks then is not known; nothing is written for it.";
|
|
1518
|
+
readonly 'user-file-uncertain': "apply --user: a library the tool calls decides where its user file is (platformdirs for Poetry and PDM on macOS), and its version cannot be read from files, so apply --user writes where the tool's docs say and names the other files the tool may read.";
|
|
1519
|
+
readonly 'user-setting-overridden': "apply --user: a file the tool reads ahead of its user file (~/.npmrc for pnpm 10) sets a key apply writes there to another value, so the value written has no effect.";
|
|
1520
|
+
readonly 'version-not-used': "apply --user: --for gives the version of a tool that apply --user does not configure (not in PATH with targets \"auto\", or not listed under targets).";
|
|
634
1521
|
readonly 'cooldown-not-written': "The release-age delay cannot be written for a tool.";
|
|
635
1522
|
readonly 'cooldown-shortened': "A release-age delay set outside depcurfew's markers (in JSON files also one an earlier apply wrote) is longer than the policy, and apply replaces it with the shorter one.";
|
|
636
1523
|
readonly 'cooldown-environment-only': "pip reads no project configuration, so the release-age delay has to be set in the environment where pip runs.";
|
|
637
1524
|
readonly 'exclude-pattern-unsupported': "A cooldown exception uses a wildcard the tool does not support, so it is not written.";
|
|
638
1525
|
readonly 'exclude-unsupported': "The tool has no setting for cooldown exceptions, so the release-age delay applies to every package.";
|
|
639
1526
|
readonly 'allow-pattern-unsupported': "An installScripts.allow entry is a pattern, but the tool takes package names only, so it is not written.";
|
|
1527
|
+
readonly 'deny-pattern-unsupported': "An installScripts.deny entry is a pattern, but the tool records denials by package name only, so it is not written.";
|
|
640
1528
|
readonly 'allow-all-unsupported': "The tool has no setting that allows every install script, so installScripts \"allow\" is not written.";
|
|
641
|
-
readonly 'allow-all-replaced': "A setting that allowed every install script was
|
|
642
|
-
readonly 'allow-list-entry-outside-policy': "An install-script allow-list in the repository lets a package run scripts the policy does not allow; apply does not remove it.";
|
|
1529
|
+
readonly 'allow-all-replaced': "A setting that allowed every install script (with any value the tool reads as true, also one that a YAML merge key brings in, or pnpm 10's dangerously-allow-all-builds in .npmrc) was set to false, because the policy denies install scripts.";
|
|
1530
|
+
readonly 'allow-list-entry-outside-policy': "An install-script allow-list in the repository (with apply --user, in a user file) lets a package run scripts the policy does not allow; apply does not remove it.";
|
|
1531
|
+
readonly 'deny-conflicts-with-file': "An entry in a tool's own list lets a package of installScripts.deny run its install scripts, or blocks a package of installScripts.allow; apply does not change entries it did not write.";
|
|
1532
|
+
readonly 'unexpected-value': "A setting apply adds to has a value of a type the tool does not accept there, so apply leaves it as it is and writes nothing into it.";
|
|
643
1533
|
readonly 'built-in-list-replaced': "Bun's built-in list of trusted packages is replaced by the policy's allow-list.";
|
|
644
|
-
readonly 'scripts-not-enforceable': "The pinned npm or pnpm runs every install script and has no allow-list, so \"deny\" cannot be enforced.";
|
|
645
|
-
readonly '
|
|
646
|
-
readonly '
|
|
1534
|
+
readonly 'scripts-not-enforceable': "The pinned (with apply --user, the installed) npm or pnpm, or another npm the repository runs (one a declared Node.js comes with, or a CI job or Dockerfile installs), runs every install script and has no allow-list, so \"deny\" cannot be enforced; also given for an npm the repository runs whose version depcurfew cannot tell, which counts as one that runs them.";
|
|
1535
|
+
readonly 'strict-allow-scripts-for-npm-in-use': "An npm the repository runs (the pinned one, one a CI job or Dockerfile installs, or one a declared Node.js comes with, for a project also through the root version files and root Dockerfiles that build it) is 11.16 to 11.x and runs install scripts the allow-list does not cover unless strict-allow-scripts is set, so apply writes strict-allow-scripts=true, which makes npm 12 stop the install at such a script; not given when that npm is the pinned one and no place runs npm 12.";
|
|
1536
|
+
readonly 'before-overrides-cooldown': "before= in .npmrc takes the place of the release-age delay apply writes, or makes npm stop with an error next to it (apply --user then does not write the delay).";
|
|
1537
|
+
readonly 'exact-version-gap': "The pinned (with apply --user, the installed) pnpm skips the release-age delay for packages requested by exact version.";
|
|
647
1538
|
readonly 'source-not-enforceable': "The tool has no setting for a dependency source the policy denies; doctor checks the lockfile instead.";
|
|
648
1539
|
readonly 'source-switch-indirect-only': "The pnpm setting for git and tarball sources stops them only for indirect dependencies.";
|
|
649
1540
|
readonly 'source-switch-combined': "One pnpm setting covers git and tarball sources, so denying one denies both.";
|
|
@@ -658,6 +1549,7 @@ declare const APPLY_WARNINGS: {
|
|
|
658
1549
|
readonly 'bot-config-missing': "A bot has no configuration file, and depcurfew does not create one.";
|
|
659
1550
|
readonly 'bot-config-not-edited': "A bot configuration file is in a place or format depcurfew does not edit; a snippet shows what to add.";
|
|
660
1551
|
readonly 'bot-entries-missing': "The Dependabot configuration has no entries for the package managers in use.";
|
|
1552
|
+
readonly 'bot-rule-shortens-delay': "A Renovate packageRules entry apply does not manage gives every package of a registry, or some update types, a shorter release-age delay than the one apply writes; Renovate uses it, and apply does not change it.";
|
|
661
1553
|
readonly 'security-delay-unsupported': "Dependabot cannot delay security updates, so securityUpdates \"delay\" is not applied.";
|
|
662
1554
|
};
|
|
663
1555
|
declare const APPLY_ERRORS: {
|
|
@@ -669,11 +1561,21 @@ declare const APPLY_ERRORS: {
|
|
|
669
1561
|
readonly 'policy-exists': "depcurfew init found a depcurfew.json and writes it only with --force.";
|
|
670
1562
|
readonly 'directory-not-found': "The directory given on the command line does not exist or cannot be opened.";
|
|
671
1563
|
readonly 'not-a-directory': "The path given on the command line is a file, not a directory.";
|
|
1564
|
+
readonly usage: "The command line is not valid: an unknown command, option or hook, a missing or extra argument, an option without its value or with one it does not take (such as a --registry that is not an http:// or https:// URL), or options that cannot go together (exit code 2); with --json the document holds only schemaVersion and this error.";
|
|
1565
|
+
readonly 'project-not-found': "A directory listed in projects in depcurfew.json does not exist, so nothing is written.";
|
|
1566
|
+
readonly 'project-not-a-directory': "A path listed in projects in depcurfew.json is a file, not a directory, so nothing is written.";
|
|
1567
|
+
readonly 'project-symlink': "A directory listed in projects in depcurfew.json is a symlink or lies under one, which may lead out of the repository, so nothing is written.";
|
|
1568
|
+
readonly 'project-has-policy': "A directory listed in projects has a depcurfew.json of its own, a second policy for the same files, so nothing is written.";
|
|
1569
|
+
readonly 'project-is-repository': "A directory listed in projects is a separate git repository or lies in one (it has a .git of its own, as a git submodule does), whose files belong to that repository, so nothing is written.";
|
|
1570
|
+
readonly 'project-in-workspace': "A directory listed in projects is, holds or lies in a workspace package of the repository root, which the root configuration already covers, so nothing is written.";
|
|
1571
|
+
readonly 'home-missing': "apply --user and init --user: the home directory (HOME, USERPROFILE on Windows) does not exist or is not an absolute path, so nothing is written.";
|
|
672
1572
|
};
|
|
673
1573
|
type ApplyWarningCode = keyof typeof APPLY_WARNINGS;
|
|
674
1574
|
type ApplyErrorCode = keyof typeof APPLY_ERRORS;
|
|
675
1575
|
interface ApplyWarning {
|
|
676
1576
|
code: ApplyWarningCode;
|
|
1577
|
+
/** The directory listed in projects that the warning concerns; absent for the repository root. */
|
|
1578
|
+
project?: string;
|
|
677
1579
|
/** The tool it concerns, when there is one. */
|
|
678
1580
|
tool?: ToolId;
|
|
679
1581
|
/** The file it concerns, when there is one, and the line when it is known. */
|
|
@@ -708,7 +1610,7 @@ declare class WriteFailed extends Error {
|
|
|
708
1610
|
readonly unrestored: string[];
|
|
709
1611
|
/** For files in `unrestored`: the temporary file, next to it, that holds its earlier text. */
|
|
710
1612
|
readonly backups: Readonly<Record<string, string>>;
|
|
711
|
-
/** Temporary files that could not be removed, relative to the root. */
|
|
1613
|
+
/** Temporary files that could not be removed, named like `file` (relative to the root, or as WriteOptions.name says). */
|
|
712
1614
|
readonly leftovers: string[];
|
|
713
1615
|
/** Another program holds the file, or (on Windows) it is read-only. */
|
|
714
1616
|
readonly busy: boolean;
|
|
@@ -730,7 +1632,20 @@ interface EditFailure {
|
|
|
730
1632
|
file: string;
|
|
731
1633
|
message: string;
|
|
732
1634
|
}
|
|
1635
|
+
/** Why a directory listed in projects cannot be used; nothing is written and doctor reports it (invalid-project). */
|
|
1636
|
+
interface ProjectProblem {
|
|
1637
|
+
code: Extract<ApplyErrorCode, `project-${string}`>;
|
|
1638
|
+
/** The entry of projects it concerns. */
|
|
1639
|
+
project: string;
|
|
1640
|
+
location: {
|
|
1641
|
+
file: string;
|
|
1642
|
+
line?: number;
|
|
1643
|
+
};
|
|
1644
|
+
message: string;
|
|
1645
|
+
fix: string;
|
|
1646
|
+
}
|
|
733
1647
|
interface ApplyReport {
|
|
1648
|
+
/** Repository-relative paths, also for the files of the directories listed in projects. */
|
|
734
1649
|
changes: FileChange[];
|
|
735
1650
|
/** The warnings as one line of text each. */
|
|
736
1651
|
warnings: string[];
|
|
@@ -741,17 +1656,68 @@ interface ApplyReport {
|
|
|
741
1656
|
errors: string[];
|
|
742
1657
|
/** The same failures with the tool and the file. */
|
|
743
1658
|
failures: EditFailure[];
|
|
1659
|
+
/**
|
|
1660
|
+
* Directories listed in projects that cannot be used (decision D-100); their messages come first in `errors`, and
|
|
1661
|
+
* nothing is written when there is one.
|
|
1662
|
+
*/
|
|
1663
|
+
problems?: ApplyProblem[];
|
|
1664
|
+
}
|
|
1665
|
+
/** An error that concerns no file being edited (a directory listed in projects, the home directory of apply --user). */
|
|
1666
|
+
interface ApplyProblem {
|
|
1667
|
+
code: ApplyErrorCode;
|
|
1668
|
+
location: {
|
|
1669
|
+
file: string;
|
|
1670
|
+
line?: number;
|
|
1671
|
+
};
|
|
1672
|
+
message: string;
|
|
1673
|
+
fix: string;
|
|
1674
|
+
}
|
|
1675
|
+
/** How a directory is planned in a repository with projects (decision D-100). */
|
|
1676
|
+
interface PlanOptions {
|
|
1677
|
+
/** The directory is the project listed so in projects (relative to the repository root): bots are not configured there. */
|
|
1678
|
+
project?: string;
|
|
1679
|
+
/** The directory is the root of a repository with projects: what the projects configure, which bots serve too. */
|
|
1680
|
+
projects?: {
|
|
1681
|
+
managerMinutes: readonly number[];
|
|
1682
|
+
targets: ReadonlySet<ToolId>;
|
|
1683
|
+
};
|
|
1684
|
+
/**
|
|
1685
|
+
* The project's installs in CI jobs of the repository root (working-directory: frontend), with their Node.js setup
|
|
1686
|
+
* steps, for the npm the project runs (decision D-105); jobsInProject gives them.
|
|
1687
|
+
*/
|
|
1688
|
+
ci?: {
|
|
1689
|
+
jobs: readonly WorkflowJob[];
|
|
1690
|
+
versions: readonly VersionDeclaration[];
|
|
1691
|
+
};
|
|
1692
|
+
/**
|
|
1693
|
+
* What the project takes from the repository root (decision D-109): the root's Node.js version files when it has none
|
|
1694
|
+
* of its own, and the stages of root Dockerfiles that build it; rootPlaces gives them.
|
|
1695
|
+
*/
|
|
1696
|
+
root?: RootPlaces;
|
|
744
1697
|
}
|
|
745
1698
|
/** The bots' delay: the policy's, or longer if a package manager rounded its delay up further. */
|
|
746
1699
|
declare function botDuration(policy: Duration, managerMinutes: readonly number[]): Duration;
|
|
747
|
-
/**
|
|
748
|
-
|
|
1700
|
+
/**
|
|
1701
|
+
* Computes every change of one directory without writing anything. With projects in the policy, `options` says which
|
|
1702
|
+
* part of the repository this directory is (planRepository plans them all).
|
|
1703
|
+
*/
|
|
1704
|
+
declare function planApply(root: string, policy: ResolvedPolicy, detection: Detection, tools?: readonly ToolKnowledge[], options?: PlanOptions): ApplyReport;
|
|
1705
|
+
/**
|
|
1706
|
+
* Plans the whole repository: the root as planApply does, and each directory listed in projects as a project of its
|
|
1707
|
+
* own with the same policy (decision D-100). Bots are configured only from the root, with a delay at least as long
|
|
1708
|
+
* as any package manager of any project gets. Every path is relative to the root. A listed directory that cannot be
|
|
1709
|
+
* used is an error (`problems`), so that nothing is written; the other directories are still planned, so one run
|
|
1710
|
+
* reports everything.
|
|
1711
|
+
*/
|
|
1712
|
+
declare function planRepository(root: string, policy: ResolvedPolicy, tools?: readonly ToolKnowledge[], rootDetection?: Detection): ApplyReport;
|
|
749
1713
|
declare class PolicyError extends Error {
|
|
750
1714
|
readonly issues: string[];
|
|
751
1715
|
readonly code: ApplyErrorCode;
|
|
752
1716
|
/** What to do, when the message does not say it. */
|
|
753
1717
|
readonly fix: string | undefined;
|
|
754
|
-
|
|
1718
|
+
/** The line of the policy file the first problem concerns, when it can be found (decision D-116). */
|
|
1719
|
+
readonly line: number | undefined;
|
|
1720
|
+
constructor(message: string, issues?: string[], code?: ApplyErrorCode, fix?: string, line?: number);
|
|
755
1721
|
/** The message and what to do, as one line of text. */
|
|
756
1722
|
get text(): string;
|
|
757
1723
|
}
|
|
@@ -769,7 +1735,14 @@ declare class ApplyWriteError extends Error {
|
|
|
769
1735
|
/** Files already replaced that could not be put back (normally none). */
|
|
770
1736
|
readonly unrestored: string[];
|
|
771
1737
|
readonly report: ApplyReport;
|
|
772
|
-
|
|
1738
|
+
/**
|
|
1739
|
+
* `command` is what the fix tells the user to run again (depcurfew apply --user for user files), and
|
|
1740
|
+
* `versionControl` whether the files may be in version control, to restore them from.
|
|
1741
|
+
*/
|
|
1742
|
+
constructor(failure: WriteFailed, report: ApplyReport, options?: {
|
|
1743
|
+
command?: string;
|
|
1744
|
+
versionControl?: boolean;
|
|
1745
|
+
});
|
|
773
1746
|
}
|
|
774
1747
|
/**
|
|
775
1748
|
* Reads the policy, detects the project, plans the changes and (unless told otherwise) writes them. If any file
|
|
@@ -778,9 +1751,8 @@ declare class ApplyWriteError extends Error {
|
|
|
778
1751
|
*/
|
|
779
1752
|
declare function applyProject(root: string, options?: {
|
|
780
1753
|
write?: boolean;
|
|
1754
|
+
beforeWrite?: () => void;
|
|
781
1755
|
}): ApplyReport;
|
|
782
|
-
/** A unified diff with three lines of context; empty when the texts are equal. `before` is null for a new file. */
|
|
783
|
-
declare function unifiedDiff(file: string, before: string | null, after: string, context?: number): string;
|
|
784
1756
|
interface SimilarName {
|
|
785
1757
|
/** The popular package the name resembles. */
|
|
786
1758
|
match: string;
|
|
@@ -824,6 +1796,8 @@ interface PackageReport {
|
|
|
824
1796
|
/** A spec with what the install command changes about the version or the build. */
|
|
825
1797
|
interface CheckRequest {
|
|
826
1798
|
spec: string;
|
|
1799
|
+
/** The package manager of the command, when a command names one (the hook): its own registry settings apply. */
|
|
1800
|
+
tool?: ToolId;
|
|
827
1801
|
/** npm: the dist-tag npm prefers for a name without a version or with a range; the registry's default when left out. */
|
|
828
1802
|
tag?: string;
|
|
829
1803
|
/** The package manager is not npm, so tag= in .npmrc (CheckOptions.defaultTag) does not choose its version. */
|
|
@@ -832,6 +1806,11 @@ interface CheckRequest {
|
|
|
832
1806
|
prereleases?: 'allow' | 'deny';
|
|
833
1807
|
/** Python: the package is built from source even when it has wheels (true), or only wheels are used (false). */
|
|
834
1808
|
buildFromSource?: boolean;
|
|
1809
|
+
/**
|
|
1810
|
+
* npm: the command line has an option that may set workspaces (--no-workspaces, --workspaces=false), with which npm
|
|
1811
|
+
* reads the .npmrc of a workspace package as its project configuration (decision D-124).
|
|
1812
|
+
*/
|
|
1813
|
+
workspacesOption?: boolean;
|
|
835
1814
|
}
|
|
836
1815
|
interface CheckOptions {
|
|
837
1816
|
policy: ResolvedPolicy;
|
|
@@ -852,10 +1831,155 @@ interface CheckOptions {
|
|
|
852
1831
|
* written by the package's publisher and should not reach the model as part of depcurfew's reason.
|
|
853
1832
|
*/
|
|
854
1833
|
scriptBodies?: boolean;
|
|
1834
|
+
/** Stops the lookups: checkSpecs then rejects with the signal's reason (a cancelled call of depcurfew mcp). */
|
|
1835
|
+
signal?: AbortSignal;
|
|
1836
|
+
/** Lookups shared with other checks of the same run (the two policies of the hook), by registry and name. */
|
|
1837
|
+
lookups?: LookupCache;
|
|
1838
|
+
/**
|
|
1839
|
+
* npm packages not to look up: their scope's registry is set in a file depcurfew does not take (decision D-113), so
|
|
1840
|
+
* the name may be private and the public registry may hold a look-alike. The package is reported as unverified.
|
|
1841
|
+
*/
|
|
1842
|
+
skipLookup?: (name: string, tool: ToolId | undefined, request?: Omit<CheckRequest, 'spec'>) => boolean;
|
|
855
1843
|
}
|
|
856
1844
|
declare function checkSpec(request: string | CheckRequest, options: CheckOptions): Promise<PackageReport>;
|
|
857
|
-
/**
|
|
1845
|
+
/**
|
|
1846
|
+
* Checks the specs a few at a time; the reports keep the order of the specs. When `options.signal` aborts, no further
|
|
1847
|
+
* spec is looked up and the promise rejects with the signal's reason.
|
|
1848
|
+
*/
|
|
858
1849
|
declare function checkSpecs(specs: readonly (string | CheckRequest)[], options: CheckOptions): Promise<PackageReport[]>;
|
|
1850
|
+
interface Paint {
|
|
1851
|
+
red(text: string): string;
|
|
1852
|
+
yellow(text: string): string;
|
|
1853
|
+
green(text: string): string;
|
|
1854
|
+
cyan(text: string): string;
|
|
1855
|
+
bold(text: string): string;
|
|
1856
|
+
dim(text: string): string;
|
|
1857
|
+
}
|
|
1858
|
+
interface CheckDependencies {
|
|
1859
|
+
fetch: FetchLike;
|
|
1860
|
+
now: () => Date;
|
|
1861
|
+
/** The whole of stdin, for hook mode. */
|
|
1862
|
+
stdin: () => Promise<string>;
|
|
1863
|
+
/** The user's home directory, with the user's .npmrc. */
|
|
1864
|
+
home?: () => string;
|
|
1865
|
+
/** The project root Claude Code gives hooks (CLAUDE_PROJECT_DIR); tests replace it. */
|
|
1866
|
+
projectDir?: () => string | undefined;
|
|
1867
|
+
/** --user: platform, home directory and environment variables (tests replace them). */
|
|
1868
|
+
userEnvironment?: () => UserEnvironment;
|
|
1869
|
+
/** depcurfew mcp: the streams the server talks over, its environment and signals (tests replace them). */
|
|
1870
|
+
mcpProcess?: () => McpProcess;
|
|
1871
|
+
}
|
|
1872
|
+
declare function run(argv: readonly string[], output: CliIo, dependencies?: CheckDependencies): Promise<number>;
|
|
1873
|
+
declare function getVersion(): string;
|
|
1874
|
+
interface DiffOptions {
|
|
1875
|
+
/** Shows the values of secrets as <redacted> in every line (redactSecrets), for a diff that people or logs see. */
|
|
1876
|
+
redact?: boolean;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* A unified diff with three lines of context; empty when the texts are equal. `before` is null for a new file.
|
|
1880
|
+
* Control characters in the file name are shown as text (printable), so that a name cannot start a line of its own
|
|
1881
|
+
* and forge a header or a hunk. A file with an absolute path (a user file outside the home directory) is named as it
|
|
1882
|
+
* is, without the a/ and b/ of repository paths.
|
|
1883
|
+
*/
|
|
1884
|
+
declare function unifiedDiff(file: string, before: string | null, after: string, context?: number, options?: DiffOptions): string;
|
|
1885
|
+
/**
|
|
1886
|
+
* Where a command runs, relative to the directory the command line starts in: the directories it changes to, in order
|
|
1887
|
+
* (an absolute one starts over). `localPrefix`: npm --prefix names npm's local prefix, whose .npmrc npm reads without
|
|
1888
|
+
* looking for a package.json above it. `entered`: the indexes of the steps that the shell changes to (cd, pushd,
|
|
1889
|
+
* Set-Location), which fail and leave the shell where it was when the directory does not exist, so the hook checks that
|
|
1890
|
+
* it does (decision D-119); a directory that a mkdir earlier on the line creates is left out. `physical`: the indexes of
|
|
1891
|
+
* the steps resolved through symlinks (cd -P, set -P), as the system resolves them. `assumes`: directories the shell
|
|
1892
|
+
* changed to earlier on the line and left again (cd -, popd, cd /path), each of which must exist for this directory to
|
|
1893
|
+
* be right, as a change that fails leaves OLDPWD and the stack of pushd as they were (decision D-123); the hook checks
|
|
1894
|
+
* their entered steps too.
|
|
1895
|
+
*/
|
|
1896
|
+
interface KnownDirectory {
|
|
1897
|
+
steps: readonly string[];
|
|
1898
|
+
localPrefix?: true;
|
|
1899
|
+
entered?: readonly number[];
|
|
1900
|
+
physical?: readonly number[];
|
|
1901
|
+
assumes?: readonly KnownDirectory[];
|
|
1902
|
+
/**
|
|
1903
|
+
* In `assumes`: a Python virtual environment whose activation script the command runs with . or source, which the
|
|
1904
|
+
* hook trusts not to change directory only when this directory holds pyvenv.cfg (decision D-123).
|
|
1905
|
+
*/
|
|
1906
|
+
venv?: true;
|
|
1907
|
+
}
|
|
1908
|
+
/** A directory as KnownDirectory gives it, or why it cannot be told. */
|
|
1909
|
+
type CommandDirectory = KnownDirectory | {
|
|
1910
|
+
unknown: string;
|
|
1911
|
+
};
|
|
1912
|
+
/** Why a relative directory that the shell finds when it gets to it (pushd -n) cannot be followed, by the variables of the line. */
|
|
1913
|
+
interface RelativeDoubt {
|
|
1914
|
+
variables: ShellVariables;
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* A change of directory by one command (decision D-123 for the stack): cd; cd - (`back`, with `push` for pushd -);
|
|
1918
|
+
* pushd dir (`keep`: pushd -n, which only puts dir on the stack; no path: Push-Location alone, which pushes the location
|
|
1919
|
+
* without changing it); pushd without a directory (`swap`); pushd +N or -N (`rotate`, `fromRight` for -N); popd with
|
|
1920
|
+
* +N or -N (`count`); dirs -c (`clear`); set -P or set +P (`mode`), which make later changes resolve the path physically
|
|
1921
|
+
* or not. `physical`: cd -P or cd -L chose. `stack`: the stack of pushd is unknown too. `lasting`: the directory of every
|
|
1922
|
+
* later command is unknown, whatever changes it later. `doubt`: the variables of the line, for a directory pushd -n put
|
|
1923
|
+
* on the stack, which the shell finds relative to where it is when it gets to it. `activate`: . or source of the
|
|
1924
|
+
* activation script of the virtual environment in `root`, which changes no directory when `root` holds pyvenv.cfg;
|
|
1925
|
+
* `reason` is why the directory is unknown when it cannot be trusted (decision D-123).
|
|
1926
|
+
*/
|
|
1927
|
+
type Move = {
|
|
1928
|
+
kind: 'cd';
|
|
1929
|
+
path: string;
|
|
1930
|
+
physical?: boolean;
|
|
1931
|
+
} | {
|
|
1932
|
+
kind: 'back';
|
|
1933
|
+
command: string;
|
|
1934
|
+
push?: true;
|
|
1935
|
+
} | {
|
|
1936
|
+
kind: 'push';
|
|
1937
|
+
path: string | undefined;
|
|
1938
|
+
keep?: true;
|
|
1939
|
+
} | {
|
|
1940
|
+
kind: 'swap';
|
|
1941
|
+
command: string;
|
|
1942
|
+
doubt?: RelativeDoubt;
|
|
1943
|
+
} | {
|
|
1944
|
+
kind: 'rotate';
|
|
1945
|
+
command: string;
|
|
1946
|
+
count: number;
|
|
1947
|
+
fromRight: boolean;
|
|
1948
|
+
keep: boolean;
|
|
1949
|
+
doubt?: RelativeDoubt;
|
|
1950
|
+
} | {
|
|
1951
|
+
kind: 'pop';
|
|
1952
|
+
command: string;
|
|
1953
|
+
count?: number;
|
|
1954
|
+
fromRight?: boolean;
|
|
1955
|
+
keep?: boolean;
|
|
1956
|
+
doubt?: RelativeDoubt;
|
|
1957
|
+
} | {
|
|
1958
|
+
kind: 'clear';
|
|
1959
|
+
} | {
|
|
1960
|
+
kind: 'mode';
|
|
1961
|
+
physical: boolean;
|
|
1962
|
+
} | {
|
|
1963
|
+
kind: 'unknown';
|
|
1964
|
+
reason: string;
|
|
1965
|
+
stack?: true;
|
|
1966
|
+
lasting?: true;
|
|
1967
|
+
} | {
|
|
1968
|
+
kind: 'activate';
|
|
1969
|
+
root: string;
|
|
1970
|
+
reason: string;
|
|
1971
|
+
};
|
|
1972
|
+
/**
|
|
1973
|
+
* Variables the command line sets that change where cd goes: CDPATH, OLDPWD (cd -), PWD and DIRSTACK (popd).
|
|
1974
|
+
* `dynamic`: why a variable whose name the shell builds may be set, which may be any of them (decision D-123).
|
|
1975
|
+
*/
|
|
1976
|
+
interface ShellVariables {
|
|
1977
|
+
cdPath: boolean;
|
|
1978
|
+
oldPwd: boolean;
|
|
1979
|
+
pwd: boolean;
|
|
1980
|
+
dirStack: boolean;
|
|
1981
|
+
dynamic?: string;
|
|
1982
|
+
}
|
|
859
1983
|
interface InstallRequest {
|
|
860
1984
|
tool: ToolId;
|
|
861
1985
|
/** The command as written, for messages. */
|
|
@@ -867,11 +1991,36 @@ interface InstallRequest {
|
|
|
867
1991
|
prereleases?: 'allow' | 'deny';
|
|
868
1992
|
/** Python: the command makes the tool build the package from source (true) or install wheels only (false). */
|
|
869
1993
|
buildFromSource?: boolean;
|
|
1994
|
+
/** Where the install runs, when the command changes directory before it or moves it with an option (decision D-118). */
|
|
1995
|
+
directory?: CommandDirectory;
|
|
1996
|
+
}
|
|
1997
|
+
/** An option or variable of a command that weakens the release-age delay or the rule on install scripts (installOverrides). */
|
|
1998
|
+
interface Override {
|
|
1999
|
+
tool: ToolId;
|
|
2000
|
+
/** The command it is on, for messages. */
|
|
2001
|
+
command: string;
|
|
2002
|
+
/** As written: --min-release-age=0, npm_config_ignore_scripts=false. */
|
|
2003
|
+
written: string;
|
|
2004
|
+
weakens: OverrideSwitch['weakens'];
|
|
2005
|
+
/** The delay it sets, to compare with the policy; left out when any value weakens it. */
|
|
2006
|
+
delay?: string;
|
|
2007
|
+
/** The shell expands its value. */
|
|
2008
|
+
expanded?: boolean;
|
|
2009
|
+
/** Where its command runs, when that is not where the command line starts (decision D-118). */
|
|
2010
|
+
directory?: CommandDirectory;
|
|
870
2011
|
}
|
|
871
2012
|
interface CommandScan {
|
|
872
2013
|
requests: InstallRequest[];
|
|
873
2014
|
/** Why an install command could not be read; undefined when every one could. */
|
|
874
2015
|
unreadable: string | undefined;
|
|
2016
|
+
/** Options and variables that weaken what depcurfew apply configured, to be compared with the policy. */
|
|
2017
|
+
overrides?: Override[];
|
|
2018
|
+
/** A command on the line changes the package manager's configuration; it counts when the line installs packages. */
|
|
2019
|
+
changes?: string;
|
|
2020
|
+
/** A package manager runs on the line, so variables that weaken it count. */
|
|
2021
|
+
manager?: boolean;
|
|
2022
|
+
/** How the command changes the directory of the shell that runs it (cd, pushd, eval 'cd …'), in order. */
|
|
2023
|
+
moves?: Move[];
|
|
875
2024
|
}
|
|
876
2025
|
interface ScanOptions {
|
|
877
2026
|
/** The shell that runs the line: a POSIX shell (the default; also Git Bash) or PowerShell. */
|
|
@@ -884,94 +2033,28 @@ declare function scanCommandLine(line: string, options?: ScanOptions): CommandSc
|
|
|
884
2033
|
* problems with individual files become warnings.
|
|
885
2034
|
*/
|
|
886
2035
|
declare function detectProject(root: string, tools?: readonly ToolKnowledge[], environment?: EnvironmentKnowledge): Detection;
|
|
887
|
-
declare class Repository {
|
|
888
|
-
#private;
|
|
889
|
-
readonly root: string;
|
|
890
|
-
readonly warnings: DetectionWarning[];
|
|
891
|
-
constructor(root: string);
|
|
892
|
-
warn(code: string, message: string, files: readonly string[]): void;
|
|
893
|
-
isFile(path: string): boolean;
|
|
894
|
-
/** The text of a file, without a byte order mark; undefined (with a warning when useful) if it cannot be read. */
|
|
895
|
-
readText(path: string, limit?: number): string | undefined;
|
|
896
|
-
/** The first bytes of a file, e.g. to recognize a lockfile format without reading a large file. */
|
|
897
|
-
readHead(path: string, bytes?: number): string | undefined;
|
|
898
|
-
/** Names of the files in a directory (following symlinks only inside the repository). */
|
|
899
|
-
listFiles(path: string): string[];
|
|
900
|
-
/** Names of the subdirectories of a directory, skipping node_modules, .git and symlinks that lead outside. */
|
|
901
|
-
listDirectories(path: string): string[];
|
|
902
|
-
/** Parses a file with the given parser, reporting failures as warnings. */
|
|
903
|
-
parse<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
904
|
-
/** Like parse, but each file is read and parsed once per parser: several rules read the same file. */
|
|
905
|
-
parseOnce<T>(path: string, parser: (text: string) => T): T | undefined;
|
|
906
|
-
relative(absolute: string): string;
|
|
907
|
-
}
|
|
908
|
-
/** The SARIF levels: an error fails `doctor`, warnings and notes do not. */
|
|
909
|
-
type Level = 'error' | 'warning' | 'note';
|
|
910
|
-
interface Location {
|
|
911
|
-
/** Repository-relative path with `/` separators. */
|
|
912
|
-
file: string;
|
|
913
|
-
/** 1-based; absent when the finding concerns the whole file. */
|
|
914
|
-
line?: number;
|
|
915
|
-
}
|
|
916
|
-
interface Finding {
|
|
917
|
-
rule: string;
|
|
918
|
-
level: Level;
|
|
919
|
-
message: string;
|
|
920
|
-
location: Location;
|
|
921
|
-
/** A fix for this finding; the rule's general help applies when absent. */
|
|
922
|
-
fix?: string;
|
|
923
|
-
}
|
|
924
|
-
/** Something doctor could not check, such as an unreadable file. Not a problem in the repository itself. */
|
|
925
|
-
interface Notice {
|
|
926
|
-
message: string;
|
|
927
|
-
file?: string;
|
|
928
|
-
}
|
|
929
|
-
interface RuleContext {
|
|
930
|
-
root: string;
|
|
931
|
-
repository: Repository;
|
|
932
|
-
detection: Detection;
|
|
933
|
-
/** undefined when the repository has no depcurfew.json. */
|
|
934
|
-
policy: ResolvedPolicy | undefined;
|
|
935
|
-
tools: readonly ToolKnowledge[];
|
|
936
|
-
report(finding: Omit<Finding, 'rule' | 'level'> & {
|
|
937
|
-
level?: Level;
|
|
938
|
-
}): void;
|
|
939
|
-
notice(message: string, file?: string): void;
|
|
940
|
-
}
|
|
941
|
-
interface Rule {
|
|
942
|
-
/** Stable kebab-case identifier, used in SARIF and in the documentation. */
|
|
943
|
-
id: string;
|
|
944
|
-
/** One sentence: what the rule finds. */
|
|
945
|
-
summary: string;
|
|
946
|
-
/** Why it matters. */
|
|
947
|
-
description: string;
|
|
948
|
-
/** How to fix it in general. */
|
|
949
|
-
help: string;
|
|
950
|
-
/** Default level of its findings. */
|
|
951
|
-
level: Level;
|
|
952
|
-
/** Rules that compare the repository with depcurfew.json do not run without one. */
|
|
953
|
-
requiresPolicy: boolean;
|
|
954
|
-
check(context: RuleContext): void;
|
|
955
|
-
}
|
|
956
|
-
interface DoctorReport {
|
|
957
|
-
schemaVersion: 1;
|
|
958
|
-
/** invalid: depcurfew.json does not follow the schema or cannot be read (only with invalidPolicy: 'report'). */
|
|
959
|
-
policy: 'valid' | 'missing' | 'invalid';
|
|
960
|
-
findings: Finding[];
|
|
961
|
-
notices: Notice[];
|
|
962
|
-
}
|
|
963
2036
|
/** Every doctor rule, in the order they are documented. unreadable-config runs last, after every other rule has read its files. */
|
|
964
2037
|
declare const RULES: readonly Rule[];
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
2038
|
+
type FailOn = (typeof FAIL_ON_LEVELS)[number];
|
|
2039
|
+
/** Where the setting came from: the command line (or the Action input), doctor.failOn of the policy, or neither. */
|
|
2040
|
+
type FailOnFrom = 'option' | 'policy' | 'default';
|
|
2041
|
+
interface FailOnSetting {
|
|
2042
|
+
failOn: FailOn;
|
|
2043
|
+
from: FailOnFrom;
|
|
2044
|
+
/** from option: the option as given, "--strict" or "--fail-on warning". */
|
|
2045
|
+
option?: string;
|
|
2046
|
+
/** from policy: the policy file as messages show it, e.g. depcurfew.json. */
|
|
2047
|
+
file?: string;
|
|
972
2048
|
}
|
|
973
2049
|
declare function countLevels(findings: readonly Finding[]): Record<'error' | 'warning' | 'note', number>;
|
|
974
|
-
|
|
2050
|
+
/**
|
|
2051
|
+
* `failOn`: which findings fail doctor and where that was set (decision D-112); when warnings make it fail, a line under
|
|
2052
|
+
* the counts says why, with how to fail on errors only. `strict` alone (before D-112) adds STRICT_NOTE.
|
|
2053
|
+
*/
|
|
2054
|
+
declare function renderText(report: DoctorReport, rules: readonly Rule[], colors?: Paint, options?: {
|
|
2055
|
+
strict?: boolean;
|
|
2056
|
+
failOn?: FailOnSetting;
|
|
2057
|
+
}): string;
|
|
975
2058
|
/** The text of a line in a repository file, to give regions the end column GitHub asks for. */
|
|
976
2059
|
type LineReader = (file: string, line: number) => string | undefined;
|
|
977
2060
|
/** Why doctor could not run, with a stable code. */
|
|
@@ -984,11 +2067,15 @@ declare function toSarif(report: DoctorReport, rules: readonly Rule[], version:
|
|
|
984
2067
|
/**
|
|
985
2068
|
* Runs the rules on a repository. When depcurfew.json exists but is invalid, it throws PolicyError, or with
|
|
986
2069
|
* invalidPolicy: 'report' runs the rules that need no policy and reports the problems as an invalid-policy finding.
|
|
2070
|
+
* `policy` passes the policy in instead of reading depcurfew.json (null: run as without one). With projects in the
|
|
2071
|
+
* policy, the rules also run on every listed directory with the same policy, and the report covers them all, with
|
|
2072
|
+
* paths relative to the root (decision D-100).
|
|
987
2073
|
*/
|
|
988
2074
|
declare function runDoctor(root: string, options?: {
|
|
989
2075
|
rules?: readonly Rule[];
|
|
990
2076
|
tools?: readonly ToolKnowledge[];
|
|
991
2077
|
invalidPolicy?: 'throw' | 'report';
|
|
2078
|
+
policy?: ResolvedPolicy | null;
|
|
992
2079
|
}): DoctorReport;
|
|
993
2080
|
type NumericDelayUnit = Exclude<DelayUnit, 'duration-string'>;
|
|
994
2081
|
declare const DURATION_STRING_UNITS: readonly ["minutes", "hours", "days", "weeks"];
|
|
@@ -1033,6 +2120,7 @@ declare const DEFAULT_POLICY: {
|
|
|
1033
2120
|
readonly installScripts: {
|
|
1034
2121
|
readonly default: "deny";
|
|
1035
2122
|
readonly allow: readonly [];
|
|
2123
|
+
readonly deny: readonly [];
|
|
1036
2124
|
};
|
|
1037
2125
|
readonly sources: {
|
|
1038
2126
|
readonly git: "deny";
|
|
@@ -1041,6 +2129,9 @@ declare const DEFAULT_POLICY: {
|
|
|
1041
2129
|
readonly directory: "allow";
|
|
1042
2130
|
};
|
|
1043
2131
|
readonly targets: "auto";
|
|
2132
|
+
readonly doctor: {
|
|
2133
|
+
readonly failOn: "warning";
|
|
2134
|
+
};
|
|
1044
2135
|
};
|
|
1045
2136
|
declare function renderPolicy(policy: Policy): string;
|
|
1046
2137
|
type PolicyIssue = Issue;
|
|
@@ -1056,4 +2147,4 @@ declare function validatePolicy(document: unknown, tools?: readonly ToolKnowledg
|
|
|
1056
2147
|
/** Parses and validates the text of a policy file. */
|
|
1057
2148
|
declare function parsePolicy(source: string, tools?: readonly ToolKnowledge[]): PolicyResult;
|
|
1058
2149
|
declare function formatIssue(issue: PolicyIssue): string;
|
|
1059
|
-
export { APPLY_ERRORS, APPLY_WARNINGS, type ApplyErrorCode, type ApplyReport, type ApplyWarning, type ApplyWarningCode, ApplyWriteError, type CheckFinding, type CheckOptions, type CliIo, type CommandScan, type Converted, type CooldownConversion, DEFAULT_POLICY, type Detection, type DetectionWarning, type DoctorReport, type Duration, type DurationResult, type DurationStringUnit, EXIT_FAILURE, EXIT_OK, EXIT_USAGE, type FileChange, type Finding, type FindingCode, type InstallRequest, type Level, type Location, type Notice, type NumericDelayUnit, POLICY_FILE, type PackageManagerInfo, type PackageReport, type Policy, PolicyError, type PolicyIssue, type PolicyResult, PopularNames, RULES, type ResolvedPolicy, type Rule, SCHEMA_URL, type ScanOptions, type Snippet, type VersionDeclaration, applyProject, botDuration, checkSpec, checkSpecs, convertCooldown, convertMinutes, countLevels, detectProject, formatDuration, formatIssue, getVersion, loadPolicy, parseDuration, parsePolicy, planApply, renderPolicy, renderText, run, runDoctor, scanCommandLine, toDurationString, toSarif, unifiedDiff, validatePolicy, warningText };
|
|
2150
|
+
export { APPLY_ERRORS, APPLY_WARNINGS, type ApplyErrorCode, type ApplyProblem, type ApplyReport, type ApplyWarning, type ApplyWarningCode, ApplyWriteError, type CheckFinding, type CheckOptions, type CliIo, type CommandDirectory, type CommandScan, type Converted, type CooldownConversion, DEFAULT_POLICY, type Detection, type DetectionWarning, type DoctorReport, type Duration, type DurationResult, type DurationStringUnit, EXIT_FAILURE, EXIT_OK, EXIT_USAGE, type FileChange, type Finding, type FindingCode, type InstallRequest, type KnownDirectory, type Level, type Location, type Notice, type NumericDelayUnit, POLICY_FILE, type PackageManagerInfo, type PackageReport, type PlanOptions, type Policy, PolicyError, type PolicyIssue, type PolicyResult, PopularNames, type ProjectProblem, RULES, type ResolvedPolicy, type Rule, SCHEMA_URL, type ScanOptions, type Snippet, type VersionDeclaration, applyProject, botDuration, checkSpec, checkSpecs, convertCooldown, convertMinutes, countLevels, detectProject, formatDuration, formatIssue, getVersion, loadPolicy, parseDuration, parsePolicy, planApply, planRepository, renderPolicy, renderText, run, runDoctor, scanCommandLine, toDurationString, toSarif, unifiedDiff, validatePolicy, warningText };
|