codegate-ai 0.1.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/LICENSE +22 -0
- package/README.md +390 -0
- package/dist/cli-prompts.d.ts +6 -0
- package/dist/cli-prompts.js +94 -0
- package/dist/cli.d.ts +64 -0
- package/dist/cli.js +443 -0
- package/dist/commands/run-policy.d.ts +27 -0
- package/dist/commands/run-policy.js +39 -0
- package/dist/commands/scan-command/helpers.d.ts +28 -0
- package/dist/commands/scan-command/helpers.js +233 -0
- package/dist/commands/scan-command.d.ts +90 -0
- package/dist/commands/scan-command.js +403 -0
- package/dist/commands/undo.d.ts +5 -0
- package/dist/commands/undo.js +14 -0
- package/dist/config.d.ts +50 -0
- package/dist/config.js +187 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/knowledge-base/claude-code.json +152 -0
- package/dist/knowledge-base/cline.json +224 -0
- package/dist/knowledge-base/codex.json +162 -0
- package/dist/knowledge-base/copilot.json +132 -0
- package/dist/knowledge-base/cursor.json +134 -0
- package/dist/knowledge-base/gemini-cli.json +112 -0
- package/dist/knowledge-base/jetbrains-junie.json +208 -0
- package/dist/knowledge-base/kiro.json +102 -0
- package/dist/knowledge-base/opencode.json +128 -0
- package/dist/knowledge-base/roo-code.json +116 -0
- package/dist/knowledge-base/schema.json +77 -0
- package/dist/knowledge-base/windsurf.json +80 -0
- package/dist/knowledge-base/zed.json +88 -0
- package/dist/layer1-discovery/config-parser.d.ts +12 -0
- package/dist/layer1-discovery/config-parser.js +52 -0
- package/dist/layer1-discovery/file-walker.d.ts +13 -0
- package/dist/layer1-discovery/file-walker.js +77 -0
- package/dist/layer1-discovery/knowledge-base.d.ts +36 -0
- package/dist/layer1-discovery/knowledge-base.js +58 -0
- package/dist/layer1-discovery/tool-detector.d.ts +20 -0
- package/dist/layer1-discovery/tool-detector.js +138 -0
- package/dist/layer2-static/detectors/command-exec.d.ts +11 -0
- package/dist/layer2-static/detectors/command-exec.js +343 -0
- package/dist/layer2-static/detectors/consent-bypass.d.ts +8 -0
- package/dist/layer2-static/detectors/consent-bypass.js +330 -0
- package/dist/layer2-static/detectors/env-override.d.ts +8 -0
- package/dist/layer2-static/detectors/env-override.js +132 -0
- package/dist/layer2-static/detectors/git-hooks.d.ts +11 -0
- package/dist/layer2-static/detectors/git-hooks.js +61 -0
- package/dist/layer2-static/detectors/ide-settings.d.ts +8 -0
- package/dist/layer2-static/detectors/ide-settings.js +66 -0
- package/dist/layer2-static/detectors/plugin-manifest.d.ts +9 -0
- package/dist/layer2-static/detectors/plugin-manifest.js +1943 -0
- package/dist/layer2-static/detectors/rule-file.d.ts +7 -0
- package/dist/layer2-static/detectors/rule-file.js +299 -0
- package/dist/layer2-static/detectors/symlink.d.ts +9 -0
- package/dist/layer2-static/detectors/symlink.js +45 -0
- package/dist/layer2-static/engine.d.ts +28 -0
- package/dist/layer2-static/engine.js +83 -0
- package/dist/layer2-static/evidence.d.ts +12 -0
- package/dist/layer2-static/evidence.js +128 -0
- package/dist/layer2-static/rule-engine.d.ts +24 -0
- package/dist/layer2-static/rule-engine.js +138 -0
- package/dist/layer2-static/state/scan-state.d.ts +32 -0
- package/dist/layer2-static/state/scan-state.js +296 -0
- package/dist/layer3-dynamic/command-builder.d.ts +15 -0
- package/dist/layer3-dynamic/command-builder.js +39 -0
- package/dist/layer3-dynamic/local-text-analysis.d.ts +19 -0
- package/dist/layer3-dynamic/local-text-analysis.js +73 -0
- package/dist/layer3-dynamic/meta-agent.d.ts +17 -0
- package/dist/layer3-dynamic/meta-agent.js +33 -0
- package/dist/layer3-dynamic/prompt-templates/local-text-analysis.md +32 -0
- package/dist/layer3-dynamic/prompt-templates/security-analysis.md +13 -0
- package/dist/layer3-dynamic/prompt-templates/tool-poisoning.md +15 -0
- package/dist/layer3-dynamic/resource-fetcher.d.ts +25 -0
- package/dist/layer3-dynamic/resource-fetcher.js +119 -0
- package/dist/layer3-dynamic/sandbox.d.ts +13 -0
- package/dist/layer3-dynamic/sandbox.js +40 -0
- package/dist/layer3-dynamic/tool-description-acquisition.d.ts +22 -0
- package/dist/layer3-dynamic/tool-description-acquisition.js +76 -0
- package/dist/layer3-dynamic/tool-description-scanner.d.ts +11 -0
- package/dist/layer3-dynamic/tool-description-scanner.js +53 -0
- package/dist/layer3-dynamic/toxic-flow.d.ts +12 -0
- package/dist/layer3-dynamic/toxic-flow.js +57 -0
- package/dist/layer4-remediation/actions/quarantine.d.ts +1 -0
- package/dist/layer4-remediation/actions/quarantine.js +8 -0
- package/dist/layer4-remediation/actions/remove-field.d.ts +5 -0
- package/dist/layer4-remediation/actions/remove-field.js +53 -0
- package/dist/layer4-remediation/actions/replace-value.d.ts +5 -0
- package/dist/layer4-remediation/actions/replace-value.js +26 -0
- package/dist/layer4-remediation/actions/strip-unicode.d.ts +5 -0
- package/dist/layer4-remediation/actions/strip-unicode.js +8 -0
- package/dist/layer4-remediation/backup-manager.d.ts +32 -0
- package/dist/layer4-remediation/backup-manager.js +138 -0
- package/dist/layer4-remediation/diff-generator.d.ts +6 -0
- package/dist/layer4-remediation/diff-generator.js +29 -0
- package/dist/layer4-remediation/remediation-runner.d.ts +36 -0
- package/dist/layer4-remediation/remediation-runner.js +230 -0
- package/dist/layer4-remediation/remediator.d.ts +36 -0
- package/dist/layer4-remediation/remediator.js +117 -0
- package/dist/path-display.d.ts +1 -0
- package/dist/path-display.js +20 -0
- package/dist/pipeline.d.ts +34 -0
- package/dist/pipeline.js +259 -0
- package/dist/report-summary.d.ts +6 -0
- package/dist/report-summary.js +48 -0
- package/dist/reporter/html.d.ts +2 -0
- package/dist/reporter/html.js +103 -0
- package/dist/reporter/json.d.ts +2 -0
- package/dist/reporter/json.js +3 -0
- package/dist/reporter/markdown.d.ts +2 -0
- package/dist/reporter/markdown.js +52 -0
- package/dist/reporter/sarif.d.ts +2 -0
- package/dist/reporter/sarif.js +84 -0
- package/dist/reporter/terminal.d.ts +5 -0
- package/dist/reporter/terminal.js +94 -0
- package/dist/runtime/signal-handlers.d.ts +10 -0
- package/dist/runtime/signal-handlers.js +17 -0
- package/dist/scan-target/helpers.d.ts +20 -0
- package/dist/scan-target/helpers.js +268 -0
- package/dist/scan-target/staging.d.ts +5 -0
- package/dist/scan-target/staging.js +114 -0
- package/dist/scan-target/types.d.ts +18 -0
- package/dist/scan-target/types.js +1 -0
- package/dist/scan-target.d.ts +3 -0
- package/dist/scan-target.js +31 -0
- package/dist/scan.d.ts +54 -0
- package/dist/scan.js +593 -0
- package/dist/tui/app.d.ts +10 -0
- package/dist/tui/app.js +21 -0
- package/dist/tui/theme.d.ts +8 -0
- package/dist/tui/theme.js +7 -0
- package/dist/tui/views/dashboard.d.ts +6 -0
- package/dist/tui/views/dashboard.js +8 -0
- package/dist/tui/views/deep-scan-consent.d.ts +5 -0
- package/dist/tui/views/deep-scan-consent.js +6 -0
- package/dist/tui/views/progress.d.ts +4 -0
- package/dist/tui/views/progress.js +6 -0
- package/dist/tui/views/summary.d.ts +5 -0
- package/dist/tui/views/summary.js +16 -0
- package/dist/types/discovery.d.ts +12 -0
- package/dist/types/discovery.js +1 -0
- package/dist/types/finding.d.ts +46 -0
- package/dist/types/finding.js +15 -0
- package/dist/types/report.d.ts +25 -0
- package/dist/types/report.js +23 -0
- package/dist/wrapper.d.ts +35 -0
- package/dist/wrapper.js +220 -0
- package/package.json +97 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { applyConfigPolicy } from "../config.js";
|
|
5
|
+
import { buildMetaAgentCommand, } from "../layer3-dynamic/command-builder.js";
|
|
6
|
+
import { buildPromptEvidenceText, supportsToollessLocalTextAnalysis, } from "../layer3-dynamic/local-text-analysis.js";
|
|
7
|
+
import { buildLocalTextAnalysisPrompt, buildSecurityAnalysisPrompt, } from "../layer3-dynamic/meta-agent.js";
|
|
8
|
+
import { layer3OutcomesToFindings, mergeLayer3Findings, runDeepScanWithConsent, } from "../pipeline.js";
|
|
9
|
+
import { mergeMetaAgentMetadata, metadataSummary, noEligibleDeepResourceNotes, parseLocalTextFindings, parseMetaAgentOutput, remediationSummaryLines, renderByFormat, withMetaAgentFinding, } from "./scan-command/helpers.js";
|
|
10
|
+
function toMetaAgentPreference(value) {
|
|
11
|
+
const normalized = value.trim().toLowerCase();
|
|
12
|
+
if (normalized === "claude" || normalized === "claude-code") {
|
|
13
|
+
return "claude";
|
|
14
|
+
}
|
|
15
|
+
if (normalized === "codex" || normalized === "codex-cli") {
|
|
16
|
+
return "codex";
|
|
17
|
+
}
|
|
18
|
+
if (normalized === "opencode") {
|
|
19
|
+
return "opencode";
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function deepAgentOptions(report, config) {
|
|
24
|
+
const detected = new Set(report.tools_detected);
|
|
25
|
+
const skipTools = new Set(config.tool_discovery.skip_tools.map((tool) => tool.trim().toLowerCase()));
|
|
26
|
+
const preferred = toMetaAgentPreference(config.tool_discovery.preferred_agent);
|
|
27
|
+
const candidates = [];
|
|
28
|
+
if (detected.has("claude-code") && !skipTools.has("claude") && !skipTools.has("claude-code")) {
|
|
29
|
+
candidates.push({
|
|
30
|
+
id: "claude",
|
|
31
|
+
label: "Claude Code",
|
|
32
|
+
metaTool: "claude",
|
|
33
|
+
binary: config.tool_discovery.agent_paths.claude ??
|
|
34
|
+
config.tool_discovery.agent_paths["claude-code"] ??
|
|
35
|
+
"claude",
|
|
36
|
+
detectedTool: "claude-code",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (detected.has("codex-cli") && !skipTools.has("codex") && !skipTools.has("codex-cli")) {
|
|
40
|
+
candidates.push({
|
|
41
|
+
id: "codex",
|
|
42
|
+
label: "Codex CLI",
|
|
43
|
+
metaTool: "codex",
|
|
44
|
+
binary: config.tool_discovery.agent_paths.codex ??
|
|
45
|
+
config.tool_discovery.agent_paths["codex-cli"] ??
|
|
46
|
+
"codex",
|
|
47
|
+
detectedTool: "codex-cli",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (detected.has("opencode") && !skipTools.has("opencode")) {
|
|
51
|
+
candidates.push({
|
|
52
|
+
id: "opencode",
|
|
53
|
+
label: "OpenCode",
|
|
54
|
+
metaTool: "generic",
|
|
55
|
+
binary: config.tool_discovery.agent_paths.opencode ?? "opencode",
|
|
56
|
+
detectedTool: "opencode",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const order = ["claude", "codex", "opencode"];
|
|
60
|
+
return candidates.sort((left, right) => {
|
|
61
|
+
if (preferred && left.id === preferred && right.id !== preferred) {
|
|
62
|
+
return -1;
|
|
63
|
+
}
|
|
64
|
+
if (preferred && right.id === preferred && left.id !== preferred) {
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
return order.indexOf(left.id) - order.indexOf(right.id);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export async function executeScanCommand(input, deps) {
|
|
71
|
+
try {
|
|
72
|
+
const interactivePromptsEnabled = deps.isTTY() && input.options.noTui !== true;
|
|
73
|
+
const discoveryContext = deps.prepareScanDiscovery
|
|
74
|
+
? await deps.prepareScanDiscovery(input.scanTarget, input.config, {
|
|
75
|
+
explicitCandidates: input.explicitCandidates,
|
|
76
|
+
})
|
|
77
|
+
: undefined;
|
|
78
|
+
let report = await deps.runScan({
|
|
79
|
+
version: input.version,
|
|
80
|
+
scanTarget: input.scanTarget,
|
|
81
|
+
config: input.config,
|
|
82
|
+
flags: input.options,
|
|
83
|
+
discoveryContext,
|
|
84
|
+
});
|
|
85
|
+
if (input.displayTarget && input.displayTarget !== report.scan_target) {
|
|
86
|
+
report = {
|
|
87
|
+
...report,
|
|
88
|
+
scan_target: input.displayTarget,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const deepScanNotes = [];
|
|
92
|
+
if (input.options.deep) {
|
|
93
|
+
const discoverResources = deps.discoverDeepResources ?? (async () => []);
|
|
94
|
+
const discoverLocalTextTargets = deps.discoverLocalTextTargets ?? (async () => []);
|
|
95
|
+
const resources = await discoverResources(input.scanTarget, input.config, discoveryContext);
|
|
96
|
+
const localTextTargets = await discoverLocalTextTargets(input.scanTarget, input.config, discoveryContext);
|
|
97
|
+
const optionsForAgent = deepAgentOptions(report, input.config);
|
|
98
|
+
let selectedAgent = null;
|
|
99
|
+
if ((resources.length > 0 || localTextTargets.length > 0) && optionsForAgent.length > 0) {
|
|
100
|
+
if (input.options.force || !interactivePromptsEnabled) {
|
|
101
|
+
selectedAgent = optionsForAgent[0] ?? null;
|
|
102
|
+
}
|
|
103
|
+
else if (deps.requestDeepAgentSelection) {
|
|
104
|
+
selectedAgent = await deps.requestDeepAgentSelection(optionsForAgent);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (resources.length === 0 && localTextTargets.length === 0) {
|
|
108
|
+
deepScanNotes.push(...noEligibleDeepResourceNotes());
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
if (selectedAgent) {
|
|
112
|
+
deepScanNotes.push(`Deep scan meta-agent selected: ${selectedAgent.label} (${selectedAgent.binary})`);
|
|
113
|
+
}
|
|
114
|
+
else if (optionsForAgent.length > 0) {
|
|
115
|
+
deepScanNotes.push("Deep scan meta-agent skipped. Running deterministic Layer 3 checks only.");
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
deepScanNotes.push("No supported deep-scan agent detected (Claude Code, Codex CLI, or OpenCode). Running deterministic Layer 3 checks only.");
|
|
119
|
+
}
|
|
120
|
+
if (resources.length > 0) {
|
|
121
|
+
if (!deps.executeDeepResource) {
|
|
122
|
+
throw new Error("Deep resource executor not configured");
|
|
123
|
+
}
|
|
124
|
+
let resourcesWithFetchedMetadata = 0;
|
|
125
|
+
let executedMetaAgentCommands = 0;
|
|
126
|
+
const outcomes = await runDeepScanWithConsent(resources, async (resource) => {
|
|
127
|
+
if (input.options.force) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (deps.requestDeepScanConsent) {
|
|
131
|
+
return await deps.requestDeepScanConsent(resource);
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}, async (resource) => {
|
|
135
|
+
const fetched = await deps.executeDeepResource(resource);
|
|
136
|
+
if (fetched.status !== "ok" || !selectedAgent) {
|
|
137
|
+
return fetched;
|
|
138
|
+
}
|
|
139
|
+
resourcesWithFetchedMetadata += 1;
|
|
140
|
+
const prompt = buildSecurityAnalysisPrompt({
|
|
141
|
+
resourceId: resource.id,
|
|
142
|
+
resourceSummary: metadataSummary(fetched.metadata),
|
|
143
|
+
});
|
|
144
|
+
const command = buildMetaAgentCommand({
|
|
145
|
+
tool: selectedAgent.metaTool,
|
|
146
|
+
prompt,
|
|
147
|
+
workingDirectory: input.scanTarget,
|
|
148
|
+
binaryPath: selectedAgent.binary,
|
|
149
|
+
});
|
|
150
|
+
const commandContext = {
|
|
151
|
+
resource,
|
|
152
|
+
agent: selectedAgent,
|
|
153
|
+
command,
|
|
154
|
+
};
|
|
155
|
+
const approvedCommand = input.options.force ||
|
|
156
|
+
(deps.requestMetaAgentCommandConsent
|
|
157
|
+
? await deps.requestMetaAgentCommandConsent(commandContext)
|
|
158
|
+
: false);
|
|
159
|
+
if (!approvedCommand) {
|
|
160
|
+
return {
|
|
161
|
+
...fetched,
|
|
162
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
163
|
+
id: `layer3-meta-agent-skipped-${resource.id}`,
|
|
164
|
+
severity: "INFO",
|
|
165
|
+
description: `Deep scan meta-agent command skipped for ${resource.id}`,
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (!deps.runMetaAgentCommand) {
|
|
170
|
+
throw new Error("Meta-agent command runner not configured");
|
|
171
|
+
}
|
|
172
|
+
executedMetaAgentCommands += 1;
|
|
173
|
+
const commandResult = await deps.runMetaAgentCommand(commandContext);
|
|
174
|
+
if (commandResult.code !== 0) {
|
|
175
|
+
return {
|
|
176
|
+
...fetched,
|
|
177
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
178
|
+
id: `layer3-meta-agent-command-error-${resource.id}`,
|
|
179
|
+
severity: "LOW",
|
|
180
|
+
description: `Deep scan meta-agent command failed for ${resource.id}`,
|
|
181
|
+
evidence: commandResult.stderr || `exit code: ${commandResult.code}`,
|
|
182
|
+
}),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const parsedOutput = parseMetaAgentOutput(commandResult.stdout);
|
|
186
|
+
if (parsedOutput === null) {
|
|
187
|
+
return {
|
|
188
|
+
...fetched,
|
|
189
|
+
metadata: withMetaAgentFinding(fetched.metadata, {
|
|
190
|
+
id: `layer3-meta-agent-parse-error-${resource.id}`,
|
|
191
|
+
severity: "LOW",
|
|
192
|
+
description: `Deep scan meta-agent output was not valid JSON for ${resource.id}`,
|
|
193
|
+
evidence: commandResult.stdout.slice(0, 400),
|
|
194
|
+
}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const normalizedOutput = Array.isArray(parsedOutput)
|
|
198
|
+
? { findings: parsedOutput }
|
|
199
|
+
: parsedOutput;
|
|
200
|
+
return {
|
|
201
|
+
...fetched,
|
|
202
|
+
metadata: mergeMetaAgentMetadata(fetched.metadata, normalizedOutput),
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
const layer3Findings = layer3OutcomesToFindings(outcomes, {
|
|
206
|
+
unicodeAnalysis: input.config.unicode_analysis,
|
|
207
|
+
});
|
|
208
|
+
report = mergeLayer3Findings(report, layer3Findings);
|
|
209
|
+
if (selectedAgent) {
|
|
210
|
+
if (resourcesWithFetchedMetadata === 0) {
|
|
211
|
+
deepScanNotes.push("Selected meta-agent was not executed because no approved resources returned metadata successfully.");
|
|
212
|
+
}
|
|
213
|
+
else if (executedMetaAgentCommands === 0) {
|
|
214
|
+
deepScanNotes.push("Selected meta-agent was not executed because meta-agent command execution was not approved.");
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const suffix = executedMetaAgentCommands === 1 ? "" : "s";
|
|
218
|
+
deepScanNotes.push(`Deep scan meta-agent executed for ${executedMetaAgentCommands} resource${suffix}.`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (localTextTargets.length > 0) {
|
|
223
|
+
if (!selectedAgent) {
|
|
224
|
+
deepScanNotes.push("Local instruction-file analysis skipped because no meta-agent was selected.");
|
|
225
|
+
}
|
|
226
|
+
else if (!supportsToollessLocalTextAnalysis(selectedAgent.metaTool)) {
|
|
227
|
+
deepScanNotes.push("Local instruction-file analysis was skipped because the selected agent does not support tool-less analysis.");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Local instruction files are analyzed as inert text only; referenced URLs stay as evidence, not inputs.
|
|
231
|
+
if (!deps.runMetaAgentCommand) {
|
|
232
|
+
throw new Error("Meta-agent command runner not configured");
|
|
233
|
+
}
|
|
234
|
+
const isolatedWorkingDirectory = mkdtempSync(join(tmpdir(), "codegate-local-analysis-"));
|
|
235
|
+
let executedLocalAnalyses = 0;
|
|
236
|
+
try {
|
|
237
|
+
for (const target of localTextTargets) {
|
|
238
|
+
const prompt = buildLocalTextAnalysisPrompt({
|
|
239
|
+
filePath: target.reportPath,
|
|
240
|
+
textContent: buildPromptEvidenceText(target.textContent),
|
|
241
|
+
referencedUrls: target.referencedUrls,
|
|
242
|
+
});
|
|
243
|
+
const command = buildMetaAgentCommand({
|
|
244
|
+
tool: selectedAgent.metaTool,
|
|
245
|
+
prompt,
|
|
246
|
+
workingDirectory: isolatedWorkingDirectory,
|
|
247
|
+
binaryPath: selectedAgent.binary,
|
|
248
|
+
});
|
|
249
|
+
command.timeoutMs = 60_000;
|
|
250
|
+
const commandContext = {
|
|
251
|
+
localFile: target,
|
|
252
|
+
agent: selectedAgent,
|
|
253
|
+
command,
|
|
254
|
+
};
|
|
255
|
+
const approvedCommand = input.options.force ||
|
|
256
|
+
(deps.requestMetaAgentCommandConsent
|
|
257
|
+
? await deps.requestMetaAgentCommandConsent(commandContext)
|
|
258
|
+
: false);
|
|
259
|
+
if (!approvedCommand) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
executedLocalAnalyses += 1;
|
|
263
|
+
const commandResult = await deps.runMetaAgentCommand(commandContext);
|
|
264
|
+
if (commandResult.code !== 0) {
|
|
265
|
+
deepScanNotes.push(`Local instruction-file analysis failed for ${target.reportPath}: ${commandResult.stderr || `exit code: ${commandResult.code}`}`);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const parsedOutput = parseMetaAgentOutput(commandResult.stdout);
|
|
269
|
+
if (parsedOutput === null) {
|
|
270
|
+
deepScanNotes.push(`Local instruction-file analysis returned invalid JSON for ${target.reportPath}.`);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const normalizedOutput = Array.isArray(parsedOutput)
|
|
274
|
+
? { findings: parsedOutput }
|
|
275
|
+
: parsedOutput;
|
|
276
|
+
const localFindings = parseLocalTextFindings(target.reportPath, normalizedOutput);
|
|
277
|
+
report = mergeLayer3Findings(report, localFindings);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
rmSync(isolatedWorkingDirectory, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
if (executedLocalAnalyses > 0) {
|
|
284
|
+
const suffix = executedLocalAnalyses === 1 ? "" : "s";
|
|
285
|
+
deepScanNotes.push(`Local instruction-file analysis executed for ${executedLocalAnalyses} file${suffix}.`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
report = applyConfigPolicy(report, input.config);
|
|
292
|
+
const remediationRequested = input.options.remediate ||
|
|
293
|
+
input.options.fixSafe ||
|
|
294
|
+
input.options.dryRun ||
|
|
295
|
+
input.options.patch;
|
|
296
|
+
let remediationResult = null;
|
|
297
|
+
const executeRemediation = async () => {
|
|
298
|
+
if (!deps.runRemediation) {
|
|
299
|
+
throw new Error("Remediation runner not configured");
|
|
300
|
+
}
|
|
301
|
+
const nextResult = await deps.runRemediation({
|
|
302
|
+
scanTarget: input.scanTarget,
|
|
303
|
+
report,
|
|
304
|
+
config: input.config,
|
|
305
|
+
flags: {
|
|
306
|
+
remediate: input.options.remediate,
|
|
307
|
+
fixSafe: input.options.fixSafe,
|
|
308
|
+
dryRun: input.options.dryRun,
|
|
309
|
+
patch: input.options.patch,
|
|
310
|
+
output: input.options.output,
|
|
311
|
+
},
|
|
312
|
+
isTTY: deps.isTTY(),
|
|
313
|
+
});
|
|
314
|
+
remediationResult = nextResult;
|
|
315
|
+
report = nextResult.report;
|
|
316
|
+
if (nextResult.patchContent) {
|
|
317
|
+
deps.stdout(nextResult.patchContent);
|
|
318
|
+
}
|
|
319
|
+
if (nextResult.patchPath) {
|
|
320
|
+
deps.stdout(`Patch written: ${nextResult.patchPath}`);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
if (remediationRequested) {
|
|
324
|
+
const beforeRemediation = report;
|
|
325
|
+
const hasFixableFindings = report.summary.fixable > 0;
|
|
326
|
+
if (!hasFixableFindings &&
|
|
327
|
+
input.options.remediate === true &&
|
|
328
|
+
input.options.fixSafe !== true &&
|
|
329
|
+
input.options.dryRun !== true &&
|
|
330
|
+
input.options.patch !== true) {
|
|
331
|
+
deps.stdout("No fixable findings available for remediation.");
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
const needsConsent = input.options.remediate === true &&
|
|
335
|
+
input.options.dryRun !== true &&
|
|
336
|
+
input.options.force !== true &&
|
|
337
|
+
interactivePromptsEnabled &&
|
|
338
|
+
hasFixableFindings;
|
|
339
|
+
if (needsConsent) {
|
|
340
|
+
const context = {
|
|
341
|
+
scanTarget: input.scanTarget,
|
|
342
|
+
totalFindings: report.summary.total,
|
|
343
|
+
fixableFindings: report.summary.fixable,
|
|
344
|
+
criticalFindings: report.summary.by_severity.CRITICAL ?? 0,
|
|
345
|
+
};
|
|
346
|
+
const approved = deps.requestRemediationConsent
|
|
347
|
+
? await deps.requestRemediationConsent(context)
|
|
348
|
+
: false;
|
|
349
|
+
if (approved) {
|
|
350
|
+
await executeRemediation();
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
deps.stdout("Remediation skipped by user.");
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
await executeRemediation();
|
|
358
|
+
}
|
|
359
|
+
if (remediationResult) {
|
|
360
|
+
for (const line of remediationSummaryLines({
|
|
361
|
+
scanTarget: input.scanTarget,
|
|
362
|
+
options: input.options,
|
|
363
|
+
before: beforeRemediation,
|
|
364
|
+
result: remediationResult,
|
|
365
|
+
})) {
|
|
366
|
+
deps.stdout(line);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const shouldUseTui = input.config.output_format === "terminal" &&
|
|
372
|
+
input.config.tui.enabled &&
|
|
373
|
+
!input.options.output &&
|
|
374
|
+
deps.isTTY() &&
|
|
375
|
+
deps.renderTui !== undefined;
|
|
376
|
+
if (shouldUseTui) {
|
|
377
|
+
deps.renderTui?.({ view: "dashboard", report, notices: deepScanNotes });
|
|
378
|
+
deps.renderTui?.({ view: "summary", report });
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
if (deepScanNotes.length > 0) {
|
|
382
|
+
for (const note of deepScanNotes) {
|
|
383
|
+
deps.stdout(note);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const rendered = renderByFormat(input.config.output_format, report, {
|
|
387
|
+
verbose: input.options.verbose,
|
|
388
|
+
});
|
|
389
|
+
if (input.options.output) {
|
|
390
|
+
deps.writeFile(resolve(input.cwd, input.options.output), rendered);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
deps.stdout(rendered);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
deps.setExitCode(report.summary.exit_code);
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
400
|
+
deps.stderr(`Scan failed: ${message}`);
|
|
401
|
+
deps.setExitCode(3);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { listBackupSessions, restoreBackupSession, } from "../layer4-remediation/backup-manager.js";
|
|
3
|
+
export function undoLatestSession(input) {
|
|
4
|
+
const projectRoot = resolve(input.projectRoot);
|
|
5
|
+
const sessions = listBackupSessions(projectRoot);
|
|
6
|
+
if (sessions.length === 0) {
|
|
7
|
+
throw new Error("No backup sessions found.");
|
|
8
|
+
}
|
|
9
|
+
const latestSession = sessions[0];
|
|
10
|
+
return restoreBackupSession({
|
|
11
|
+
projectRoot,
|
|
12
|
+
sessionId: latestSession,
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Finding } from "./types/finding.js";
|
|
2
|
+
import type { CodeGateReport } from "./types/report.js";
|
|
3
|
+
export declare const OUTPUT_FORMATS: readonly ["terminal", "json", "sarif", "markdown", "html"];
|
|
4
|
+
export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
|
|
5
|
+
export declare const SEVERITY_THRESHOLDS: readonly ["critical", "high", "medium", "low", "info"];
|
|
6
|
+
export type SeverityThreshold = (typeof SEVERITY_THRESHOLDS)[number];
|
|
7
|
+
export interface TuiConfig {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
colour_scheme: string;
|
|
10
|
+
compact_mode: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolDiscoveryConfig {
|
|
13
|
+
preferred_agent: string;
|
|
14
|
+
agent_paths: Record<string, string>;
|
|
15
|
+
skip_tools: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface CodeGateConfig {
|
|
18
|
+
severity_threshold: SeverityThreshold;
|
|
19
|
+
auto_proceed_below_threshold: boolean;
|
|
20
|
+
output_format: OutputFormat;
|
|
21
|
+
scan_state_path?: string;
|
|
22
|
+
scan_user_scope?: boolean;
|
|
23
|
+
tui: TuiConfig;
|
|
24
|
+
tool_discovery: ToolDiscoveryConfig;
|
|
25
|
+
trusted_directories: string[];
|
|
26
|
+
blocked_commands: string[];
|
|
27
|
+
known_safe_mcp_servers: string[];
|
|
28
|
+
known_safe_formatters: string[];
|
|
29
|
+
known_safe_lsp_servers: string[];
|
|
30
|
+
known_safe_hooks: string[];
|
|
31
|
+
unicode_analysis: boolean;
|
|
32
|
+
check_ide_settings: boolean;
|
|
33
|
+
owasp_mapping: boolean;
|
|
34
|
+
trusted_api_domains: string[];
|
|
35
|
+
suppress_findings: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface CliConfigOverrides {
|
|
38
|
+
format?: OutputFormat;
|
|
39
|
+
configPath?: string;
|
|
40
|
+
noTui?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface ResolveConfigOptions {
|
|
43
|
+
scanTarget: string;
|
|
44
|
+
homeDir?: string;
|
|
45
|
+
cli?: CliConfigOverrides;
|
|
46
|
+
}
|
|
47
|
+
export declare const DEFAULT_CONFIG: CodeGateConfig;
|
|
48
|
+
export declare function resolveEffectiveConfig(options: ResolveConfigOptions): CodeGateConfig;
|
|
49
|
+
export declare function computeExitCode(findings: Finding[], threshold: SeverityThreshold): number;
|
|
50
|
+
export declare function applyConfigPolicy(report: CodeGateReport, config: CodeGateConfig): CodeGateReport;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { parse as parseJsonc } from "jsonc-parser";
|
|
5
|
+
import { applyReportSummary, computeExitCode as computeReportExitCode } from "./report-summary.js";
|
|
6
|
+
export const OUTPUT_FORMATS = ["terminal", "json", "sarif", "markdown", "html"];
|
|
7
|
+
export const SEVERITY_THRESHOLDS = ["critical", "high", "medium", "low", "info"];
|
|
8
|
+
export const DEFAULT_CONFIG = {
|
|
9
|
+
severity_threshold: "high",
|
|
10
|
+
auto_proceed_below_threshold: true,
|
|
11
|
+
output_format: "terminal",
|
|
12
|
+
scan_state_path: "~/.codegate/scan-state.json",
|
|
13
|
+
scan_user_scope: true,
|
|
14
|
+
tui: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
colour_scheme: "default",
|
|
17
|
+
compact_mode: false,
|
|
18
|
+
},
|
|
19
|
+
tool_discovery: {
|
|
20
|
+
preferred_agent: "claude",
|
|
21
|
+
agent_paths: {},
|
|
22
|
+
skip_tools: [],
|
|
23
|
+
},
|
|
24
|
+
trusted_directories: [],
|
|
25
|
+
blocked_commands: ["bash", "sh", "curl", "wget", "nc", "python", "node"],
|
|
26
|
+
known_safe_mcp_servers: [
|
|
27
|
+
"@anthropic/mcp-server-filesystem",
|
|
28
|
+
"@modelcontextprotocol/server-github",
|
|
29
|
+
],
|
|
30
|
+
known_safe_formatters: ["prettier", "black", "gofmt", "rustfmt", "clang-format"],
|
|
31
|
+
known_safe_lsp_servers: ["typescript-language-server", "pyright", "rust-analyzer", "gopls"],
|
|
32
|
+
known_safe_hooks: [],
|
|
33
|
+
unicode_analysis: true,
|
|
34
|
+
check_ide_settings: true,
|
|
35
|
+
owasp_mapping: true,
|
|
36
|
+
trusted_api_domains: [],
|
|
37
|
+
suppress_findings: [],
|
|
38
|
+
};
|
|
39
|
+
function normalizeOutputFormat(value) {
|
|
40
|
+
if (!value) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return OUTPUT_FORMATS.find((format) => format === value) ?? undefined;
|
|
44
|
+
}
|
|
45
|
+
function normalizeSeverityThreshold(value) {
|
|
46
|
+
if (!value) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return SEVERITY_THRESHOLDS.find((threshold) => threshold === value) ?? undefined;
|
|
50
|
+
}
|
|
51
|
+
function normalizeOptionalPath(value) {
|
|
52
|
+
if (!value) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const trimmed = value.trim();
|
|
56
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
57
|
+
}
|
|
58
|
+
function unique(values) {
|
|
59
|
+
const merged = values.flatMap((entry) => entry ?? []);
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
const ordered = [];
|
|
62
|
+
for (const value of merged) {
|
|
63
|
+
if (typeof value !== "string") {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const trimmed = value.trim();
|
|
67
|
+
if (trimmed.length === 0 || seen.has(trimmed)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
seen.add(trimmed);
|
|
71
|
+
ordered.push(trimmed);
|
|
72
|
+
}
|
|
73
|
+
return ordered;
|
|
74
|
+
}
|
|
75
|
+
function readConfigFile(path) {
|
|
76
|
+
if (!existsSync(path)) {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
const raw = readFileSync(path, "utf8");
|
|
80
|
+
const parsed = parseJsonc(raw);
|
|
81
|
+
if (!parsed || typeof parsed !== "object") {
|
|
82
|
+
throw new Error(`Invalid config file: ${path}`);
|
|
83
|
+
}
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
function pickFirst(...values) {
|
|
87
|
+
for (const value of values) {
|
|
88
|
+
if (value !== undefined) {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
export function resolveEffectiveConfig(options) {
|
|
95
|
+
const home = options.homeDir ?? homedir();
|
|
96
|
+
const scanTarget = resolve(options.scanTarget);
|
|
97
|
+
const globalConfigPath = options.cli?.configPath ?? join(home, ".codegate", "config.json");
|
|
98
|
+
const globalConfig = readConfigFile(globalConfigPath);
|
|
99
|
+
const projectConfig = readConfigFile(join(scanTarget, ".codegate.json"));
|
|
100
|
+
const severity_threshold = pickFirst(normalizeSeverityThreshold(undefined), normalizeSeverityThreshold(projectConfig.severity_threshold), normalizeSeverityThreshold(globalConfig.severity_threshold), DEFAULT_CONFIG.severity_threshold) ?? DEFAULT_CONFIG.severity_threshold;
|
|
101
|
+
const output_format = pickFirst(options.cli?.format, normalizeOutputFormat(projectConfig.output_format), normalizeOutputFormat(globalConfig.output_format), DEFAULT_CONFIG.output_format) ?? DEFAULT_CONFIG.output_format;
|
|
102
|
+
return {
|
|
103
|
+
severity_threshold,
|
|
104
|
+
auto_proceed_below_threshold: pickFirst(projectConfig.auto_proceed_below_threshold, globalConfig.auto_proceed_below_threshold, DEFAULT_CONFIG.auto_proceed_below_threshold) ?? DEFAULT_CONFIG.auto_proceed_below_threshold,
|
|
105
|
+
output_format,
|
|
106
|
+
scan_state_path: pickFirst(normalizeOptionalPath(projectConfig.scan_state_path), normalizeOptionalPath(globalConfig.scan_state_path), normalizeOptionalPath(DEFAULT_CONFIG.scan_state_path)) ?? undefined,
|
|
107
|
+
scan_user_scope: pickFirst(projectConfig.scan_user_scope, globalConfig.scan_user_scope, DEFAULT_CONFIG.scan_user_scope) ?? DEFAULT_CONFIG.scan_user_scope,
|
|
108
|
+
tui: {
|
|
109
|
+
enabled: options.cli?.noTui
|
|
110
|
+
? false
|
|
111
|
+
: (pickFirst(projectConfig.tui?.enabled, globalConfig.tui?.enabled, DEFAULT_CONFIG.tui.enabled) ?? DEFAULT_CONFIG.tui.enabled),
|
|
112
|
+
colour_scheme: pickFirst(projectConfig.tui?.colour_scheme, globalConfig.tui?.colour_scheme, DEFAULT_CONFIG.tui.colour_scheme) ?? DEFAULT_CONFIG.tui.colour_scheme,
|
|
113
|
+
compact_mode: pickFirst(projectConfig.tui?.compact_mode, globalConfig.tui?.compact_mode, DEFAULT_CONFIG.tui.compact_mode) ?? DEFAULT_CONFIG.tui.compact_mode,
|
|
114
|
+
},
|
|
115
|
+
tool_discovery: {
|
|
116
|
+
preferred_agent: pickFirst(projectConfig.tool_discovery?.preferred_agent, globalConfig.tool_discovery?.preferred_agent, DEFAULT_CONFIG.tool_discovery.preferred_agent) ?? DEFAULT_CONFIG.tool_discovery.preferred_agent,
|
|
117
|
+
agent_paths: {
|
|
118
|
+
...DEFAULT_CONFIG.tool_discovery.agent_paths,
|
|
119
|
+
...(globalConfig.tool_discovery?.agent_paths ?? {}),
|
|
120
|
+
...(projectConfig.tool_discovery?.agent_paths ?? {}),
|
|
121
|
+
},
|
|
122
|
+
skip_tools: unique([
|
|
123
|
+
DEFAULT_CONFIG.tool_discovery.skip_tools,
|
|
124
|
+
globalConfig.tool_discovery?.skip_tools,
|
|
125
|
+
projectConfig.tool_discovery?.skip_tools,
|
|
126
|
+
]),
|
|
127
|
+
},
|
|
128
|
+
trusted_directories: unique([
|
|
129
|
+
DEFAULT_CONFIG.trusted_directories,
|
|
130
|
+
globalConfig.trusted_directories,
|
|
131
|
+
// Project config cannot set trusted directories.
|
|
132
|
+
]),
|
|
133
|
+
blocked_commands: unique([
|
|
134
|
+
DEFAULT_CONFIG.blocked_commands,
|
|
135
|
+
globalConfig.blocked_commands,
|
|
136
|
+
projectConfig.blocked_commands,
|
|
137
|
+
]),
|
|
138
|
+
known_safe_mcp_servers: unique([
|
|
139
|
+
DEFAULT_CONFIG.known_safe_mcp_servers,
|
|
140
|
+
globalConfig.known_safe_mcp_servers,
|
|
141
|
+
projectConfig.known_safe_mcp_servers,
|
|
142
|
+
]),
|
|
143
|
+
known_safe_formatters: unique([
|
|
144
|
+
DEFAULT_CONFIG.known_safe_formatters,
|
|
145
|
+
globalConfig.known_safe_formatters,
|
|
146
|
+
projectConfig.known_safe_formatters,
|
|
147
|
+
]),
|
|
148
|
+
known_safe_lsp_servers: unique([
|
|
149
|
+
DEFAULT_CONFIG.known_safe_lsp_servers,
|
|
150
|
+
globalConfig.known_safe_lsp_servers,
|
|
151
|
+
projectConfig.known_safe_lsp_servers,
|
|
152
|
+
]),
|
|
153
|
+
known_safe_hooks: unique([
|
|
154
|
+
DEFAULT_CONFIG.known_safe_hooks,
|
|
155
|
+
globalConfig.known_safe_hooks,
|
|
156
|
+
projectConfig.known_safe_hooks,
|
|
157
|
+
]),
|
|
158
|
+
unicode_analysis: pickFirst(projectConfig.unicode_analysis, globalConfig.unicode_analysis, DEFAULT_CONFIG.unicode_analysis) ?? DEFAULT_CONFIG.unicode_analysis,
|
|
159
|
+
check_ide_settings: pickFirst(projectConfig.check_ide_settings, globalConfig.check_ide_settings, DEFAULT_CONFIG.check_ide_settings) ?? DEFAULT_CONFIG.check_ide_settings,
|
|
160
|
+
owasp_mapping: pickFirst(projectConfig.owasp_mapping, globalConfig.owasp_mapping, DEFAULT_CONFIG.owasp_mapping) ?? DEFAULT_CONFIG.owasp_mapping,
|
|
161
|
+
trusted_api_domains: unique([
|
|
162
|
+
DEFAULT_CONFIG.trusted_api_domains,
|
|
163
|
+
globalConfig.trusted_api_domains,
|
|
164
|
+
projectConfig.trusted_api_domains,
|
|
165
|
+
]),
|
|
166
|
+
suppress_findings: unique([
|
|
167
|
+
DEFAULT_CONFIG.suppress_findings,
|
|
168
|
+
globalConfig.suppress_findings,
|
|
169
|
+
projectConfig.suppress_findings,
|
|
170
|
+
]),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export function computeExitCode(findings, threshold) {
|
|
174
|
+
return computeReportExitCode(findings, threshold);
|
|
175
|
+
}
|
|
176
|
+
export function applyConfigPolicy(report, config) {
|
|
177
|
+
const suppressionSet = new Set(config.suppress_findings);
|
|
178
|
+
const findings = report.findings.map((finding) => ({
|
|
179
|
+
...finding,
|
|
180
|
+
owasp: config.owasp_mapping ? finding.owasp : [],
|
|
181
|
+
suppressed: finding.suppressed || suppressionSet.has(finding.finding_id),
|
|
182
|
+
}));
|
|
183
|
+
return applyReportSummary({
|
|
184
|
+
...report,
|
|
185
|
+
findings,
|
|
186
|
+
}, config.severity_threshold);
|
|
187
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const APP_NAME = "codegate";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const APP_NAME = "codegate";
|