longer-agent 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 +21 -0
- package/README.md +227 -0
- package/README.zh-CN.md +227 -0
- package/agent_templates/executor/agent.yaml +22 -0
- package/agent_templates/executor/system_prompt.md +17 -0
- package/agent_templates/explorer/agent.yaml +13 -0
- package/agent_templates/explorer/system_prompt.md +19 -0
- package/agent_templates/main/agent.yaml +7 -0
- package/agent_templates/main/system_prompt.md +45 -0
- package/configExample.yaml +83 -0
- package/dist/agents/agent.d.ts +79 -0
- package/dist/agents/agent.d.ts.map +1 -0
- package/dist/agents/agent.js +156 -0
- package/dist/agents/agent.js.map +1 -0
- package/dist/agents/tool-loop.d.ts +140 -0
- package/dist/agents/tool-loop.d.ts.map +1 -0
- package/dist/agents/tool-loop.js +465 -0
- package/dist/agents/tool-loop.js.map +1 -0
- package/dist/ask.d.ts +81 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +34 -0
- package/dist/ask.js.map +1 -0
- package/dist/auth/openai-oauth.d.ts +66 -0
- package/dist/auth/openai-oauth.d.ts.map +1 -0
- package/dist/auth/openai-oauth.js +640 -0
- package/dist/auth/openai-oauth.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +254 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +118 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +862 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +130 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +648 -0
- package/dist/config.js.map +1 -0
- package/dist/context-rendering.d.ts +69 -0
- package/dist/context-rendering.d.ts.map +1 -0
- package/dist/context-rendering.js +250 -0
- package/dist/context-rendering.js.map +1 -0
- package/dist/document-projection.d.ts +12 -0
- package/dist/document-projection.d.ts.map +1 -0
- package/dist/document-projection.js +75 -0
- package/dist/document-projection.js.map +1 -0
- package/dist/ephemeral-log.d.ts +15 -0
- package/dist/ephemeral-log.d.ts.map +1 -0
- package/dist/ephemeral-log.js +173 -0
- package/dist/ephemeral-log.js.map +1 -0
- package/dist/file-attach.d.ts +89 -0
- package/dist/file-attach.d.ts.map +1 -0
- package/dist/file-attach.js +571 -0
- package/dist/file-attach.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/init-wizard.d.ts +13 -0
- package/dist/init-wizard.d.ts.map +1 -0
- package/dist/init-wizard.js +328 -0
- package/dist/init-wizard.js.map +1 -0
- package/dist/log-entry.d.ts +104 -0
- package/dist/log-entry.d.ts.map +1 -0
- package/dist/log-entry.js +292 -0
- package/dist/log-entry.js.map +1 -0
- package/dist/log-projection.d.ts +73 -0
- package/dist/log-projection.d.ts.map +1 -0
- package/dist/log-projection.js +651 -0
- package/dist/log-projection.js.map +1 -0
- package/dist/mcp-client.d.ts +55 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +402 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-selection.d.ts +16 -0
- package/dist/model-selection.d.ts.map +1 -0
- package/dist/model-selection.js +181 -0
- package/dist/model-selection.js.map +1 -0
- package/dist/network-retry.d.ts +38 -0
- package/dist/network-retry.d.ts.map +1 -0
- package/dist/network-retry.js +140 -0
- package/dist/network-retry.js.map +1 -0
- package/dist/persistence.d.ts +104 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +644 -0
- package/dist/persistence.js.map +1 -0
- package/dist/primitives/context.d.ts +29 -0
- package/dist/primitives/context.d.ts.map +1 -0
- package/dist/primitives/context.js +85 -0
- package/dist/primitives/context.js.map +1 -0
- package/dist/progress.d.ts +51 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +229 -0
- package/dist/progress.js.map +1 -0
- package/dist/provider-presets.d.ts +34 -0
- package/dist/provider-presets.d.ts.map +1 -0
- package/dist/provider-presets.js +181 -0
- package/dist/provider-presets.js.map +1 -0
- package/dist/providers/anthropic.d.ts +32 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +450 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +135 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +104 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/glm.d.ts +18 -0
- package/dist/providers/glm.d.ts.map +1 -0
- package/dist/providers/glm.js +59 -0
- package/dist/providers/glm.js.map +1 -0
- package/dist/providers/kimi.d.ts +23 -0
- package/dist/providers/kimi.d.ts.map +1 -0
- package/dist/providers/kimi.js +89 -0
- package/dist/providers/kimi.js.map +1 -0
- package/dist/providers/minimax.d.ts +20 -0
- package/dist/providers/minimax.d.ts.map +1 -0
- package/dist/providers/minimax.js +192 -0
- package/dist/providers/minimax.js.map +1 -0
- package/dist/providers/openai-chat.d.ts +33 -0
- package/dist/providers/openai-chat.d.ts.map +1 -0
- package/dist/providers/openai-chat.js +543 -0
- package/dist/providers/openai-chat.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +26 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +443 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openrouter.d.ts +24 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +177 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/registry.d.ts +7 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +38 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/security/path.d.ts +51 -0
- package/dist/security/path.d.ts.map +1 -0
- package/dist/security/path.js +187 -0
- package/dist/security/path.js.map +1 -0
- package/dist/security/sensitive-files.d.ts +3 -0
- package/dist/security/sensitive-files.d.ts.map +1 -0
- package/dist/security/sensitive-files.js +41 -0
- package/dist/security/sensitive-files.js.map +1 -0
- package/dist/session.d.ts +446 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +4595 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +46 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +134 -0
- package/dist/settings.js.map +1 -0
- package/dist/show-context.d.ts +35 -0
- package/dist/show-context.d.ts.map +1 -0
- package/dist/show-context.js +320 -0
- package/dist/show-context.js.map +1 -0
- package/dist/skills/loader.d.ts +49 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +166 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/summarize-context.d.ts +29 -0
- package/dist/summarize-context.d.ts.map +1 -0
- package/dist/summarize-context.js +247 -0
- package/dist/summarize-context.js.map +1 -0
- package/dist/templates/loader.d.ts +104 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/loader.js +514 -0
- package/dist/templates/loader.js.map +1 -0
- package/dist/tools/basic.d.ts +29 -0
- package/dist/tools/basic.d.ts.map +1 -0
- package/dist/tools/basic.js +2079 -0
- package/dist/tools/basic.js.map +1 -0
- package/dist/tools/comm.d.ts +17 -0
- package/dist/tools/comm.d.ts.map +1 -0
- package/dist/tools/comm.js +192 -0
- package/dist/tools/comm.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +237 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +24 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +51 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tui/app.d.ts +35 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +1042 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/checkbox-picker.d.ts +35 -0
- package/dist/tui/checkbox-picker.d.ts.map +1 -0
- package/dist/tui/checkbox-picker.js +85 -0
- package/dist/tui/checkbox-picker.js.map +1 -0
- package/dist/tui/command-picker.d.ts +31 -0
- package/dist/tui/command-picker.d.ts.map +1 -0
- package/dist/tui/command-picker.js +113 -0
- package/dist/tui/command-picker.js.map +1 -0
- package/dist/tui/components/ask-panel.d.ts +21 -0
- package/dist/tui/components/ask-panel.d.ts.map +1 -0
- package/dist/tui/components/ask-panel.js +81 -0
- package/dist/tui/components/ask-panel.js.map +1 -0
- package/dist/tui/components/conversation-panel.d.ts +68 -0
- package/dist/tui/components/conversation-panel.d.ts.map +1 -0
- package/dist/tui/components/conversation-panel.js +611 -0
- package/dist/tui/components/conversation-panel.js.map +1 -0
- package/dist/tui/components/input-panel.d.ts +27 -0
- package/dist/tui/components/input-panel.d.ts.map +1 -0
- package/dist/tui/components/input-panel.js +725 -0
- package/dist/tui/components/input-panel.js.map +1 -0
- package/dist/tui/components/logo-panel.d.ts +14 -0
- package/dist/tui/components/logo-panel.d.ts.map +1 -0
- package/dist/tui/components/logo-panel.js +37 -0
- package/dist/tui/components/logo-panel.js.map +1 -0
- package/dist/tui/components/plan-panel.d.ts +10 -0
- package/dist/tui/components/plan-panel.d.ts.map +1 -0
- package/dist/tui/components/plan-panel.js +8 -0
- package/dist/tui/components/plan-panel.js.map +1 -0
- package/dist/tui/components/status-bar.d.ts +24 -0
- package/dist/tui/components/status-bar.d.ts.map +1 -0
- package/dist/tui/components/status-bar.js +80 -0
- package/dist/tui/components/status-bar.js.map +1 -0
- package/dist/tui/input/editor-state.d.ts +22 -0
- package/dist/tui/input/editor-state.d.ts.map +1 -0
- package/dist/tui/input/editor-state.js +157 -0
- package/dist/tui/input/editor-state.js.map +1 -0
- package/dist/tui/input/keymap.d.ts +3 -0
- package/dist/tui/input/keymap.d.ts.map +1 -0
- package/dist/tui/input/keymap.js +72 -0
- package/dist/tui/input/keymap.js.map +1 -0
- package/dist/tui/input/paste-slots.d.ts +17 -0
- package/dist/tui/input/paste-slots.d.ts.map +1 -0
- package/dist/tui/input/paste-slots.js +46 -0
- package/dist/tui/input/paste-slots.js.map +1 -0
- package/dist/tui/input/paste.d.ts +15 -0
- package/dist/tui/input/paste.d.ts.map +1 -0
- package/dist/tui/input/paste.js +35 -0
- package/dist/tui/input/paste.js.map +1 -0
- package/dist/tui/input/protocol.d.ts +9 -0
- package/dist/tui/input/protocol.d.ts.map +1 -0
- package/dist/tui/input/protocol.js +387 -0
- package/dist/tui/input/protocol.js.map +1 -0
- package/dist/tui/input/sanitize.d.ts +6 -0
- package/dist/tui/input/sanitize.d.ts.map +1 -0
- package/dist/tui/input/sanitize.js +20 -0
- package/dist/tui/input/sanitize.js.map +1 -0
- package/dist/tui/input/types.d.ts +18 -0
- package/dist/tui/input/types.d.ts.map +1 -0
- package/dist/tui/input/types.js +2 -0
- package/dist/tui/input/types.js.map +1 -0
- package/dist/tui/launch.d.ts +23 -0
- package/dist/tui/launch.d.ts.map +1 -0
- package/dist/tui/launch.js +104 -0
- package/dist/tui/launch.js.map +1 -0
- package/dist/tui/theme.d.ts +20 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +29 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/types.d.ts +136 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +9 -0
- package/dist/tui/types.js.map +1 -0
- package/package.json +76 -0
- package/prompts/sections/agents_md.md +23 -0
- package/prompts/sections/important_log.md +16 -0
- package/prompts/sections/system_mechanisms.md +18 -0
- package/prompts/tools/apply_patch.md +31 -0
- package/prompts/tools/ask.md +18 -0
- package/prompts/tools/bash.md +13 -0
- package/prompts/tools/bash_background.md +9 -0
- package/prompts/tools/bash_output.md +9 -0
- package/prompts/tools/check_status.md +3 -0
- package/prompts/tools/diff.md +5 -0
- package/prompts/tools/edit_file.md +11 -0
- package/prompts/tools/glob.md +7 -0
- package/prompts/tools/grep.md +20 -0
- package/prompts/tools/kill_agent.md +3 -0
- package/prompts/tools/kill_shell.md +5 -0
- package/prompts/tools/list_dir.md +5 -0
- package/prompts/tools/plan.md +252 -0
- package/prompts/tools/read_file.md +9 -0
- package/prompts/tools/show_context.md +12 -0
- package/prompts/tools/skill.md +7 -0
- package/prompts/tools/spawn_agent.md +195 -0
- package/prompts/tools/summarize_context.md +122 -0
- package/prompts/tools/test.md +5 -0
- package/prompts/tools/wait.md +17 -0
- package/prompts/tools/web_fetch.md +9 -0
- package/prompts/tools/web_search.md +5 -0
- package/prompts/tools/write_file.md +11 -0
- package/skills/.staging/.gitkeep +0 -0
- package/skills/explain-code/SKILL.md +15 -0
- package/skills/skill-manager/SKILL.md +83 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter provider adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extends OpenAIChatProvider with:
|
|
5
|
+
* - Automatic base_url defaulting and HTTP-Referer / X-Title headers
|
|
6
|
+
* - OpenRouter-style reasoning params (reasoning: { effort } in extra_body)
|
|
7
|
+
* - reasoning_details extraction from non-streaming responses
|
|
8
|
+
* - reasoning_details round-trip on assistant messages
|
|
9
|
+
*/
|
|
10
|
+
import OpenAI from "openai";
|
|
11
|
+
import { OpenAIChatProvider } from "./openai-chat.js";
|
|
12
|
+
const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
13
|
+
/** Map LongerAgent thinking levels to OpenRouter reasoning effort values. */
|
|
14
|
+
const EFFORT_MAP = {
|
|
15
|
+
minimal: "minimal",
|
|
16
|
+
low: "low",
|
|
17
|
+
medium: "medium",
|
|
18
|
+
high: "high",
|
|
19
|
+
xhigh: "xhigh",
|
|
20
|
+
max: "xhigh", // Anthropic "max" → OpenRouter's highest effort
|
|
21
|
+
on: "high", // binary on/off models → default to high
|
|
22
|
+
};
|
|
23
|
+
export class OpenRouterProvider extends OpenAIChatProvider {
|
|
24
|
+
constructor(config) {
|
|
25
|
+
const headerExtra = config.extra ?? {};
|
|
26
|
+
const sanitizedExtra = Object.fromEntries(Object.entries(headerExtra).filter(([k]) => k !== "http_referer" && k !== "x_title"));
|
|
27
|
+
super({
|
|
28
|
+
...config,
|
|
29
|
+
extra: sanitizedExtra,
|
|
30
|
+
});
|
|
31
|
+
// Rebuild client with OpenRouter-specific settings
|
|
32
|
+
const baseUrl = config.baseUrl || OPENROUTER_BASE_URL;
|
|
33
|
+
const headers = {};
|
|
34
|
+
if (config.extra?.["http_referer"]) {
|
|
35
|
+
headers["HTTP-Referer"] = config.extra["http_referer"];
|
|
36
|
+
}
|
|
37
|
+
if (config.extra?.["x_title"]) {
|
|
38
|
+
headers["X-Title"] = config.extra["x_title"];
|
|
39
|
+
}
|
|
40
|
+
this._client = new OpenAI({
|
|
41
|
+
apiKey: config.apiKey,
|
|
42
|
+
baseURL: baseUrl,
|
|
43
|
+
defaultHeaders: Object.keys(headers).length > 0 ? headers : undefined,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// ------------------------------------------------------------------
|
|
47
|
+
// Thinking / reasoning params — OpenRouter unified format
|
|
48
|
+
// ------------------------------------------------------------------
|
|
49
|
+
_applyThinkingParams(kwargs, options) {
|
|
50
|
+
if (!this._config.supportsThinking)
|
|
51
|
+
return;
|
|
52
|
+
const level = options?.thinkingLevel;
|
|
53
|
+
// Explicitly disable reasoning
|
|
54
|
+
if (level === "off" || level === "none") {
|
|
55
|
+
const extraBody = kwargs["extra_body"] || {};
|
|
56
|
+
extraBody["reasoning"] = { effort: "none" };
|
|
57
|
+
kwargs["extra_body"] = extraBody;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Build reasoning config
|
|
61
|
+
const reasoningConfig = {};
|
|
62
|
+
if (this._config.thinkingBudget > 0) {
|
|
63
|
+
// Use max_tokens for reasoning when thinkingBudget is explicitly set
|
|
64
|
+
reasoningConfig["max_tokens"] = this._config.thinkingBudget;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Map thinking level to effort
|
|
68
|
+
const effort = (level && level !== "default")
|
|
69
|
+
? (EFFORT_MAP[level] ?? "high")
|
|
70
|
+
: "high";
|
|
71
|
+
reasoningConfig["effort"] = effort;
|
|
72
|
+
}
|
|
73
|
+
const extraBody = kwargs["extra_body"] || {};
|
|
74
|
+
extraBody["reasoning"] = reasoningConfig;
|
|
75
|
+
kwargs["extra_body"] = extraBody;
|
|
76
|
+
// Do NOT delete temperature or swap max_tokens → max_completion_tokens.
|
|
77
|
+
// OpenRouter normalizes these per-model internally.
|
|
78
|
+
}
|
|
79
|
+
_augmentRequestKwargs(kwargs, ctx) {
|
|
80
|
+
if (!ctx.hasNativeWebSearch)
|
|
81
|
+
return;
|
|
82
|
+
const existing = Array.isArray(kwargs["plugins"])
|
|
83
|
+
? [...kwargs["plugins"]]
|
|
84
|
+
: [];
|
|
85
|
+
const hasWebPlugin = existing.some((plugin) => plugin && typeof plugin === "object" && plugin["id"] === "web");
|
|
86
|
+
if (!hasWebPlugin) {
|
|
87
|
+
existing.push({ id: "web" });
|
|
88
|
+
}
|
|
89
|
+
kwargs["plugins"] = existing;
|
|
90
|
+
}
|
|
91
|
+
// ------------------------------------------------------------------
|
|
92
|
+
// Response post-processing — extract reasoning_details
|
|
93
|
+
// ------------------------------------------------------------------
|
|
94
|
+
async sendMessage(messages, tools, options) {
|
|
95
|
+
const result = await super.sendMessage(messages, tools, options);
|
|
96
|
+
// Non-streaming: the base class _parseResponse (private) only extracts
|
|
97
|
+
// reasoning_content (string). OpenRouter also returns reasoning_details
|
|
98
|
+
// (structured array) which we want for faithful round-tripping.
|
|
99
|
+
// Streaming already handles reasoning_details via _callStream.
|
|
100
|
+
if (result.raw && (!result.reasoningContent || result.reasoningState === result.reasoningContent)) {
|
|
101
|
+
try {
|
|
102
|
+
const raw = result.raw;
|
|
103
|
+
const choices = raw["choices"] || [];
|
|
104
|
+
if (choices.length > 0) {
|
|
105
|
+
const message = choices[0]["message"];
|
|
106
|
+
if (message) {
|
|
107
|
+
const details = message["reasoning_details"];
|
|
108
|
+
if (Array.isArray(details) && details.length > 0) {
|
|
109
|
+
const texts = [];
|
|
110
|
+
for (const item of details) {
|
|
111
|
+
if (typeof item === "string") {
|
|
112
|
+
texts.push(item);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (item && typeof item === "object") {
|
|
116
|
+
const obj = item;
|
|
117
|
+
const text = obj["content"]
|
|
118
|
+
|| obj["text"]
|
|
119
|
+
|| "";
|
|
120
|
+
if (text)
|
|
121
|
+
texts.push(text);
|
|
122
|
+
// Extract from summary arrays
|
|
123
|
+
if (Array.isArray(obj["summary"])) {
|
|
124
|
+
for (const s of obj["summary"]) {
|
|
125
|
+
const st = s["text"] || "";
|
|
126
|
+
if (st)
|
|
127
|
+
texts.push(st);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (texts.length > 0) {
|
|
133
|
+
result.reasoningContent = texts.join("\n");
|
|
134
|
+
result.reasoningState = details; // Preserve structured data for round-trip
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Ignore extraction errors — reasoning_content from base class is still usable
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
// ------------------------------------------------------------------
|
|
147
|
+
// Message conversion — reasoning_details round-trip
|
|
148
|
+
// ------------------------------------------------------------------
|
|
149
|
+
_convertMessages(messages) {
|
|
150
|
+
const converted = super._convertMessages(messages);
|
|
151
|
+
// Enrich assistant messages with reasoning_details from _reasoning_state
|
|
152
|
+
// for faithful round-tripping through OpenRouter.
|
|
153
|
+
// Use simple ordinal mapping: base class preserves assistant message order.
|
|
154
|
+
const originals = messages;
|
|
155
|
+
const origAssistantIndices = [];
|
|
156
|
+
const convAssistantIndices = [];
|
|
157
|
+
for (let i = 0; i < originals.length; i++) {
|
|
158
|
+
if (originals[i]["role"] === "assistant")
|
|
159
|
+
origAssistantIndices.push(i);
|
|
160
|
+
}
|
|
161
|
+
for (let i = 0; i < converted.length; i++) {
|
|
162
|
+
if (converted[i]["role"] === "assistant")
|
|
163
|
+
convAssistantIndices.push(i);
|
|
164
|
+
}
|
|
165
|
+
const count = Math.min(origAssistantIndices.length, convAssistantIndices.length);
|
|
166
|
+
for (let i = 0; i < count; i++) {
|
|
167
|
+
const orig = originals[origAssistantIndices[i]];
|
|
168
|
+
const conv = converted[convAssistantIndices[i]];
|
|
169
|
+
const state = orig["_reasoning_state"];
|
|
170
|
+
if (state && Array.isArray(state)) {
|
|
171
|
+
conv["reasoning_details"] = state;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return converted;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider factory — maps provider identifiers to concrete provider classes.
|
|
3
|
+
*/
|
|
4
|
+
import type { ModelConfig } from "../config.js";
|
|
5
|
+
import type { BaseProvider } from "./base.js";
|
|
6
|
+
export declare function createProvider(config: ModelConfig): BaseProvider;
|
|
7
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAS9C,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAoChE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider factory — maps provider identifiers to concrete provider classes.
|
|
3
|
+
*/
|
|
4
|
+
import { AnthropicProvider } from "./anthropic.js";
|
|
5
|
+
import { OpenAIResponsesProvider } from "./openai-responses.js";
|
|
6
|
+
import { OpenAIChatProvider } from "./openai-chat.js";
|
|
7
|
+
import { KimiProvider } from "./kimi.js";
|
|
8
|
+
import { GLMProvider } from "./glm.js";
|
|
9
|
+
import { MiniMaxProvider } from "./minimax.js";
|
|
10
|
+
import { OpenRouterProvider } from "./openrouter.js";
|
|
11
|
+
export function createProvider(config) {
|
|
12
|
+
const provider = config.provider.toLowerCase();
|
|
13
|
+
if (provider === "anthropic") {
|
|
14
|
+
return new AnthropicProvider(config);
|
|
15
|
+
}
|
|
16
|
+
if (provider === "openai" || provider === "openai-codex") {
|
|
17
|
+
return new OpenAIResponsesProvider(config);
|
|
18
|
+
}
|
|
19
|
+
if (provider === "openai-chat") {
|
|
20
|
+
return new OpenAIChatProvider(config);
|
|
21
|
+
}
|
|
22
|
+
if (provider === "kimi-cn" || provider === "kimi-ai" || provider === "kimi" || provider === "kimi-code") {
|
|
23
|
+
return new KimiProvider(config);
|
|
24
|
+
}
|
|
25
|
+
if (provider === "glm" || provider === "glm-intl" || provider === "glm-code" || provider === "glm-intl-code") {
|
|
26
|
+
return new GLMProvider(config);
|
|
27
|
+
}
|
|
28
|
+
if (provider === "minimax" || provider === "minimax-cn") {
|
|
29
|
+
return new MiniMaxProvider(config);
|
|
30
|
+
}
|
|
31
|
+
if (provider === "openrouter") {
|
|
32
|
+
return new OpenRouterProvider(config);
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Unknown provider '${config.provider}'. ` +
|
|
35
|
+
"Supported: anthropic, openai, openai-codex, openai-chat, kimi, kimi-cn, kimi-ai, kimi-code, " +
|
|
36
|
+
"glm, glm-intl, glm-code, glm-intl-code, minimax, minimax-cn, openrouter");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAE/C,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC7B,OAAO,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;QACzD,OAAO,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC/B,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QACxG,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,UAAU,IAAI,QAAQ,KAAK,UAAU,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;QAC7G,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;QACxD,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC9B,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,CAAC,QAAQ,KAAK;QACvC,8FAA8F;QAC9F,yEAAyE,CAC5E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified path safety checks for file-accessing features.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 scope:
|
|
5
|
+
* - Enforce a directory boundary (project root / session artifacts)
|
|
6
|
+
* - Prevent lexical traversal (`..`) and prefix-collision mistakes
|
|
7
|
+
* - Reject symlink escapes via canonical (realpath) checks
|
|
8
|
+
* - Support create paths by validating the nearest existing ancestor
|
|
9
|
+
*/
|
|
10
|
+
export type PathAccessKind = "read" | "write" | "list" | "search" | "attach" | "template" | "spawn_call_file" | "diff";
|
|
11
|
+
export type PathDecision = "allow" | "deny_external" | "deny_symlink";
|
|
12
|
+
export interface SafePathOptions {
|
|
13
|
+
baseDir: string;
|
|
14
|
+
requestedPath: string;
|
|
15
|
+
cwd?: string;
|
|
16
|
+
mustExist?: boolean;
|
|
17
|
+
allowCreate?: boolean;
|
|
18
|
+
expectDirectory?: boolean;
|
|
19
|
+
expectFile?: boolean;
|
|
20
|
+
accessKind: PathAccessKind;
|
|
21
|
+
followSymlinks?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SafePathResult {
|
|
24
|
+
requestedPath: string;
|
|
25
|
+
resolvedPath: string;
|
|
26
|
+
canonicalPath?: string;
|
|
27
|
+
baseDirResolved: string;
|
|
28
|
+
baseDirCanonical?: string;
|
|
29
|
+
decision: PathDecision;
|
|
30
|
+
safePath?: string;
|
|
31
|
+
reason?: string;
|
|
32
|
+
isOutsideByLexical?: boolean;
|
|
33
|
+
isOutsideByCanonical?: boolean;
|
|
34
|
+
crossedSymlinkBoundary?: boolean;
|
|
35
|
+
}
|
|
36
|
+
type SafePathErrorCode = "PATH_OUTSIDE_SCOPE" | "PATH_SYMLINK_ESCAPES_SCOPE" | "PATH_NOT_FOUND" | "PATH_NOT_FILE" | "PATH_NOT_DIRECTORY" | "PATH_INVALID_INPUT";
|
|
37
|
+
export declare class SafePathError extends Error {
|
|
38
|
+
code: SafePathErrorCode;
|
|
39
|
+
details: SafePathResult;
|
|
40
|
+
constructor(code: SafePathErrorCode, message: string, details: SafePathResult);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve and validate a path against a single allowed base directory.
|
|
44
|
+
*
|
|
45
|
+
* Phase 1 behavior:
|
|
46
|
+
* - Paths outside the base are denied
|
|
47
|
+
* - Symlink escapes are denied (future phases may map this to an `ask`)
|
|
48
|
+
*/
|
|
49
|
+
export declare function safePath(opts: SafePathOptions): SafePathResult;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/security/path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,OAAO,GACP,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,iBAAiB,GACjB,MAAM,CAAC;AAEX,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,eAAe,GACf,cAAc,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,KAAK,iBAAiB,GAClB,oBAAoB,GACpB,4BAA4B,GAC5B,gBAAgB,GAChB,eAAe,GACf,oBAAoB,GACpB,oBAAoB,CAAC;AAEzB,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;gBAGtB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc;CAO1B;AAwCD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,cAAc,CAkI9D"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified path safety checks for file-accessing features.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 scope:
|
|
5
|
+
* - Enforce a directory boundary (project root / session artifacts)
|
|
6
|
+
* - Prevent lexical traversal (`..`) and prefix-collision mistakes
|
|
7
|
+
* - Reject symlink escapes via canonical (realpath) checks
|
|
8
|
+
* - Support create paths by validating the nearest existing ancestor
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
export class SafePathError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
details;
|
|
15
|
+
constructor(code, message, details) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "SafePathError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.details = details;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function isWithinBase(baseAbs, candidateAbs) {
|
|
23
|
+
const rel = path.relative(baseAbs, candidateAbs);
|
|
24
|
+
if (rel === "")
|
|
25
|
+
return true;
|
|
26
|
+
if (path.isAbsolute(rel))
|
|
27
|
+
return false; // Windows cross-drive safety
|
|
28
|
+
return !rel.startsWith("..");
|
|
29
|
+
}
|
|
30
|
+
function nearestExistingAncestor(targetAbs) {
|
|
31
|
+
let current = targetAbs;
|
|
32
|
+
while (true) {
|
|
33
|
+
if (existsSync(current))
|
|
34
|
+
return current;
|
|
35
|
+
const parent = path.dirname(current);
|
|
36
|
+
if (parent === current)
|
|
37
|
+
return null;
|
|
38
|
+
current = parent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function makeBaseResult(opts, baseDirResolved, resolvedPath) {
|
|
42
|
+
return {
|
|
43
|
+
requestedPath: opts.requestedPath,
|
|
44
|
+
resolvedPath,
|
|
45
|
+
baseDirResolved,
|
|
46
|
+
decision: "deny_external",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function fail(code, message, result) {
|
|
50
|
+
throw new SafePathError(code, message, result);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve and validate a path against a single allowed base directory.
|
|
54
|
+
*
|
|
55
|
+
* Phase 1 behavior:
|
|
56
|
+
* - Paths outside the base are denied
|
|
57
|
+
* - Symlink escapes are denied (future phases may map this to an `ask`)
|
|
58
|
+
*/
|
|
59
|
+
export function safePath(opts) {
|
|
60
|
+
const requested = String(opts.requestedPath ?? "");
|
|
61
|
+
const baseRaw = String(opts.baseDir ?? "");
|
|
62
|
+
if (!requested.trim()) {
|
|
63
|
+
const result = {
|
|
64
|
+
requestedPath: requested,
|
|
65
|
+
resolvedPath: "",
|
|
66
|
+
baseDirResolved: path.resolve(baseRaw || "."),
|
|
67
|
+
decision: "deny_external",
|
|
68
|
+
reason: "Empty path.",
|
|
69
|
+
};
|
|
70
|
+
fail("PATH_INVALID_INPUT", "Path cannot be empty.", result);
|
|
71
|
+
}
|
|
72
|
+
if (!baseRaw.trim()) {
|
|
73
|
+
const result = {
|
|
74
|
+
requestedPath: requested,
|
|
75
|
+
resolvedPath: path.resolve(requested),
|
|
76
|
+
baseDirResolved: path.resolve("."),
|
|
77
|
+
decision: "deny_external",
|
|
78
|
+
reason: "Invalid base directory.",
|
|
79
|
+
};
|
|
80
|
+
fail("PATH_INVALID_INPUT", "Base directory cannot be empty.", result);
|
|
81
|
+
}
|
|
82
|
+
const baseDirResolved = path.resolve(baseRaw);
|
|
83
|
+
const cwd = opts.cwd ? path.resolve(opts.cwd) : process.cwd();
|
|
84
|
+
const resolvedPath = path.isAbsolute(requested)
|
|
85
|
+
? path.resolve(requested)
|
|
86
|
+
: path.resolve(cwd, requested);
|
|
87
|
+
const result = makeBaseResult(opts, baseDirResolved, resolvedPath);
|
|
88
|
+
// 1) Lexical boundary check
|
|
89
|
+
const outsideLexical = !isWithinBase(baseDirResolved, resolvedPath);
|
|
90
|
+
result.isOutsideByLexical = outsideLexical;
|
|
91
|
+
if (outsideLexical) {
|
|
92
|
+
result.reason = "Path is outside the allowed directory boundary.";
|
|
93
|
+
fail("PATH_OUTSIDE_SCOPE", result.reason, result);
|
|
94
|
+
}
|
|
95
|
+
// 2) Existence / type checks
|
|
96
|
+
const mustExist = opts.mustExist === true;
|
|
97
|
+
const allowCreate = opts.allowCreate === true;
|
|
98
|
+
const exists = existsSync(resolvedPath);
|
|
99
|
+
if (mustExist && !exists) {
|
|
100
|
+
result.reason = `Path does not exist: ${resolvedPath}`;
|
|
101
|
+
fail("PATH_NOT_FOUND", result.reason, result);
|
|
102
|
+
}
|
|
103
|
+
if (!exists && !mustExist && !allowCreate) {
|
|
104
|
+
result.reason = `Path does not exist: ${resolvedPath}`;
|
|
105
|
+
fail("PATH_NOT_FOUND", result.reason, result);
|
|
106
|
+
}
|
|
107
|
+
// 3) Canonical (realpath) boundary check to prevent symlink escapes
|
|
108
|
+
const followSymlinks = opts.followSymlinks !== false;
|
|
109
|
+
if (followSymlinks) {
|
|
110
|
+
let baseCanonical;
|
|
111
|
+
try {
|
|
112
|
+
if (existsSync(baseDirResolved)) {
|
|
113
|
+
baseCanonical = realpathSync(baseDirResolved);
|
|
114
|
+
result.baseDirCanonical = baseCanonical;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// If the base cannot be canonicalized, fall back to lexical checks.
|
|
119
|
+
}
|
|
120
|
+
if (baseCanonical) {
|
|
121
|
+
if (exists) {
|
|
122
|
+
try {
|
|
123
|
+
const candidateCanonical = realpathSync(resolvedPath);
|
|
124
|
+
result.canonicalPath = candidateCanonical;
|
|
125
|
+
const outsideCanonical = !isWithinBase(baseCanonical, candidateCanonical);
|
|
126
|
+
result.isOutsideByCanonical = outsideCanonical;
|
|
127
|
+
if (outsideCanonical) {
|
|
128
|
+
result.canonicalPath = candidateCanonical;
|
|
129
|
+
result.crossedSymlinkBoundary = true;
|
|
130
|
+
result.decision = "deny_symlink";
|
|
131
|
+
result.reason = "Path escapes the allowed directory via a symbolic link.";
|
|
132
|
+
fail("PATH_SYMLINK_ESCAPES_SCOPE", result.reason, result);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
if (e instanceof SafePathError)
|
|
137
|
+
throw e;
|
|
138
|
+
// If canonicalization fails for an existing path, rely on lexical + stat checks.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (allowCreate) {
|
|
142
|
+
const ancestor = nearestExistingAncestor(resolvedPath);
|
|
143
|
+
if (ancestor) {
|
|
144
|
+
try {
|
|
145
|
+
const ancestorCanonical = realpathSync(ancestor);
|
|
146
|
+
const outsideCanonical = !isWithinBase(baseCanonical, ancestorCanonical);
|
|
147
|
+
result.canonicalPath = ancestorCanonical;
|
|
148
|
+
result.isOutsideByCanonical = outsideCanonical;
|
|
149
|
+
if (outsideCanonical) {
|
|
150
|
+
result.crossedSymlinkBoundary = true;
|
|
151
|
+
result.decision = "deny_symlink";
|
|
152
|
+
result.reason = "Path escapes the allowed directory via a symbolic link in its parent path.";
|
|
153
|
+
fail("PATH_SYMLINK_ESCAPES_SCOPE", result.reason, result);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
if (e instanceof SafePathError)
|
|
158
|
+
throw e;
|
|
159
|
+
// ignore canonical failure; lexical check already passed
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (exists) {
|
|
166
|
+
let st;
|
|
167
|
+
try {
|
|
168
|
+
st = statSync(resolvedPath);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
result.reason = `Failed to stat path: ${e instanceof Error ? e.message : String(e)}`;
|
|
172
|
+
fail("PATH_INVALID_INPUT", result.reason, result);
|
|
173
|
+
}
|
|
174
|
+
if (opts.expectFile && !st.isFile()) {
|
|
175
|
+
result.reason = `Expected a file: ${resolvedPath}`;
|
|
176
|
+
fail("PATH_NOT_FILE", result.reason, result);
|
|
177
|
+
}
|
|
178
|
+
if (opts.expectDirectory && !st.isDirectory()) {
|
|
179
|
+
result.reason = `Expected a directory: ${resolvedPath}`;
|
|
180
|
+
fail("PATH_NOT_DIRECTORY", result.reason, result);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
result.decision = "allow";
|
|
184
|
+
result.safePath = resolvedPath;
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/security/path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-files.d.ts","sourceRoot":"","sources":["../../src/security/sensitive-files.ts"],"names":[],"mappings":"AAoBA,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwB1E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEhE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
function normalizeBasename(filePath) {
|
|
3
|
+
return path.basename(filePath).toLowerCase();
|
|
4
|
+
}
|
|
5
|
+
function isEnvSecretFile(base) {
|
|
6
|
+
if (base === ".env")
|
|
7
|
+
return true;
|
|
8
|
+
if (!base.startsWith(".env."))
|
|
9
|
+
return false;
|
|
10
|
+
if (base === ".env.example" ||
|
|
11
|
+
base === ".env.sample" ||
|
|
12
|
+
base === ".env.template" ||
|
|
13
|
+
base === ".env.schema") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
export function getSensitiveFileReadReason(filePath) {
|
|
19
|
+
const base = normalizeBasename(filePath);
|
|
20
|
+
if (isEnvSecretFile(base)) {
|
|
21
|
+
return "environment secret files (.env*) are blocked by default";
|
|
22
|
+
}
|
|
23
|
+
if (base === ".npmrc" || base === ".pypirc" || base === ".netrc") {
|
|
24
|
+
return `${base} may contain credentials`;
|
|
25
|
+
}
|
|
26
|
+
if (base === "credentials.json" || base === "application_default_credentials.json") {
|
|
27
|
+
return `${base} commonly stores cloud credentials`;
|
|
28
|
+
}
|
|
29
|
+
if (base === "id_rsa" || base === "id_dsa" || base === "id_ecdsa" || base === "id_ed25519") {
|
|
30
|
+
return `${base} is a private key file`;
|
|
31
|
+
}
|
|
32
|
+
if (base.endsWith(".pem") || base.endsWith(".key") || base.endsWith(".p12") ||
|
|
33
|
+
base.endsWith(".pfx") || base.endsWith(".p8")) {
|
|
34
|
+
return "certificate/private-key material is blocked by default";
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
export function isSensitiveFileForRead(filePath) {
|
|
39
|
+
return getSensitiveFileReadReason(filePath) !== null;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=sensitive-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-files.js","sourceRoot":"","sources":["../../src/security/sensitive-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,SAAS,iBAAiB,CAAC,QAAgB;IACzC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IACE,IAAI,KAAK,cAAc;QACvB,IAAI,KAAK,aAAa;QACtB,IAAI,KAAK,eAAe;QACxB,IAAI,KAAK,aAAa,EACtB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAgB;IACzD,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,yDAAyD,CAAC;IACnE,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjE,OAAO,GAAG,IAAI,0BAA0B,CAAC;IAC3C,CAAC;IAED,IAAI,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,sCAAsC,EAAE,CAAC;QACnF,OAAO,GAAG,IAAI,oCAAoC,CAAC;IACrD,CAAC;IAED,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC3F,OAAO,GAAG,IAAI,wBAAwB,CAAC;IACzC,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,wDAAwD,CAAC;IAClE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,OAAO,0BAA0B,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;AACvD,CAAC"}
|