@shareai-lab/kode 1.1.13 → 1.1.16
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/dist/entrypoints/cli.js +2 -1
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/index.js +5 -26
- package/dist/package.json +4 -1
- package/package.json +9 -104
- package/dist/test/testAdapters.js +0 -88
- package/dist/test/testAdapters.js.map +0 -1
- package/src/ProjectOnboarding.tsx +0 -198
- package/src/Tool.ts +0 -83
- package/src/commands/agents.tsx +0 -3416
- package/src/commands/approvedTools.ts +0 -53
- package/src/commands/bug.tsx +0 -20
- package/src/commands/clear.ts +0 -43
- package/src/commands/compact.ts +0 -120
- package/src/commands/config.tsx +0 -19
- package/src/commands/cost.ts +0 -18
- package/src/commands/ctx_viz.ts +0 -209
- package/src/commands/doctor.ts +0 -24
- package/src/commands/help.tsx +0 -19
- package/src/commands/init.ts +0 -37
- package/src/commands/listen.ts +0 -42
- package/src/commands/login.tsx +0 -51
- package/src/commands/logout.tsx +0 -40
- package/src/commands/mcp.ts +0 -41
- package/src/commands/model.tsx +0 -40
- package/src/commands/modelstatus.tsx +0 -20
- package/src/commands/onboarding.tsx +0 -34
- package/src/commands/pr_comments.ts +0 -59
- package/src/commands/refreshCommands.ts +0 -54
- package/src/commands/release-notes.ts +0 -34
- package/src/commands/resume.tsx +0 -31
- package/src/commands/review.ts +0 -49
- package/src/commands/terminalSetup.ts +0 -221
- package/src/commands.ts +0 -139
- package/src/components/ApproveApiKey.tsx +0 -93
- package/src/components/AsciiLogo.tsx +0 -13
- package/src/components/AutoUpdater.tsx +0 -148
- package/src/components/Bug.tsx +0 -367
- package/src/components/Config.tsx +0 -293
- package/src/components/ConsoleOAuthFlow.tsx +0 -327
- package/src/components/Cost.tsx +0 -23
- package/src/components/CostThresholdDialog.tsx +0 -46
- package/src/components/CustomSelect/option-map.ts +0 -42
- package/src/components/CustomSelect/select-option.tsx +0 -78
- package/src/components/CustomSelect/select.tsx +0 -152
- package/src/components/CustomSelect/theme.ts +0 -45
- package/src/components/CustomSelect/use-select-state.ts +0 -414
- package/src/components/CustomSelect/use-select.ts +0 -35
- package/src/components/FallbackToolUseRejectedMessage.tsx +0 -15
- package/src/components/FileEditToolUpdatedMessage.tsx +0 -66
- package/src/components/Help.tsx +0 -215
- package/src/components/HighlightedCode.tsx +0 -33
- package/src/components/InvalidConfigDialog.tsx +0 -113
- package/src/components/Link.tsx +0 -32
- package/src/components/LogSelector.tsx +0 -86
- package/src/components/Logo.tsx +0 -170
- package/src/components/MCPServerApprovalDialog.tsx +0 -100
- package/src/components/MCPServerDialogCopy.tsx +0 -25
- package/src/components/MCPServerMultiselectDialog.tsx +0 -109
- package/src/components/Message.tsx +0 -221
- package/src/components/MessageResponse.tsx +0 -15
- package/src/components/MessageSelector.tsx +0 -211
- package/src/components/ModeIndicator.tsx +0 -88
- package/src/components/ModelConfig.tsx +0 -301
- package/src/components/ModelListManager.tsx +0 -227
- package/src/components/ModelSelector.tsx +0 -3387
- package/src/components/ModelStatusDisplay.tsx +0 -230
- package/src/components/Onboarding.tsx +0 -274
- package/src/components/PressEnterToContinue.tsx +0 -11
- package/src/components/PromptInput.tsx +0 -760
- package/src/components/SentryErrorBoundary.ts +0 -39
- package/src/components/Spinner.tsx +0 -129
- package/src/components/StickerRequestForm.tsx +0 -16
- package/src/components/StructuredDiff.tsx +0 -191
- package/src/components/TextInput.tsx +0 -259
- package/src/components/TodoItem.tsx +0 -47
- package/src/components/TokenWarning.tsx +0 -31
- package/src/components/ToolUseLoader.tsx +0 -40
- package/src/components/TrustDialog.tsx +0 -106
- package/src/components/binary-feedback/BinaryFeedback.tsx +0 -63
- package/src/components/binary-feedback/BinaryFeedbackOption.tsx +0 -111
- package/src/components/binary-feedback/BinaryFeedbackView.tsx +0 -172
- package/src/components/binary-feedback/utils.ts +0 -220
- package/src/components/messages/AssistantBashOutputMessage.tsx +0 -22
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +0 -49
- package/src/components/messages/AssistantRedactedThinkingMessage.tsx +0 -19
- package/src/components/messages/AssistantTextMessage.tsx +0 -144
- package/src/components/messages/AssistantThinkingMessage.tsx +0 -40
- package/src/components/messages/AssistantToolUseMessage.tsx +0 -132
- package/src/components/messages/TaskProgressMessage.tsx +0 -32
- package/src/components/messages/TaskToolMessage.tsx +0 -58
- package/src/components/messages/UserBashInputMessage.tsx +0 -28
- package/src/components/messages/UserCommandMessage.tsx +0 -30
- package/src/components/messages/UserKodingInputMessage.tsx +0 -28
- package/src/components/messages/UserPromptMessage.tsx +0 -35
- package/src/components/messages/UserTextMessage.tsx +0 -39
- package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +0 -12
- package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +0 -36
- package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +0 -31
- package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +0 -57
- package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +0 -35
- package/src/components/messages/UserToolResultMessage/utils.tsx +0 -56
- package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +0 -121
- package/src/components/permissions/FallbackPermissionRequest.tsx +0 -153
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +0 -182
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +0 -77
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +0 -164
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +0 -83
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +0 -240
- package/src/components/permissions/PermissionRequest.tsx +0 -101
- package/src/components/permissions/PermissionRequestTitle.tsx +0 -69
- package/src/components/permissions/hooks.ts +0 -44
- package/src/components/permissions/toolUseOptions.ts +0 -59
- package/src/components/permissions/utils.ts +0 -23
- package/src/constants/betas.ts +0 -5
- package/src/constants/claude-asterisk-ascii-art.tsx +0 -238
- package/src/constants/figures.ts +0 -4
- package/src/constants/keys.ts +0 -3
- package/src/constants/macros.ts +0 -11
- package/src/constants/modelCapabilities.ts +0 -179
- package/src/constants/models.ts +0 -1025
- package/src/constants/oauth.ts +0 -18
- package/src/constants/product.ts +0 -17
- package/src/constants/prompts.ts +0 -168
- package/src/constants/releaseNotes.ts +0 -7
- package/src/context/PermissionContext.tsx +0 -149
- package/src/context.ts +0 -278
- package/src/cost-tracker.ts +0 -84
- package/src/entrypoints/cli.tsx +0 -1561
- package/src/entrypoints/mcp.ts +0 -175
- package/src/history.ts +0 -25
- package/src/hooks/useApiKeyVerification.ts +0 -59
- package/src/hooks/useArrowKeyHistory.ts +0 -55
- package/src/hooks/useCanUseTool.ts +0 -138
- package/src/hooks/useCancelRequest.ts +0 -39
- package/src/hooks/useDoublePress.ts +0 -41
- package/src/hooks/useExitOnCtrlCD.ts +0 -31
- package/src/hooks/useInterval.ts +0 -25
- package/src/hooks/useLogMessages.ts +0 -16
- package/src/hooks/useLogStartupTime.ts +0 -12
- package/src/hooks/useNotifyAfterTimeout.ts +0 -65
- package/src/hooks/usePermissionRequestLogging.ts +0 -44
- package/src/hooks/useTerminalSize.ts +0 -49
- package/src/hooks/useTextInput.ts +0 -317
- package/src/hooks/useUnifiedCompletion.ts +0 -1405
- package/src/index.ts +0 -34
- package/src/messages.ts +0 -38
- package/src/permissions.ts +0 -268
- package/src/query.ts +0 -720
- package/src/screens/ConfigureNpmPrefix.tsx +0 -197
- package/src/screens/Doctor.tsx +0 -219
- package/src/screens/LogList.tsx +0 -68
- package/src/screens/REPL.tsx +0 -813
- package/src/screens/ResumeConversation.tsx +0 -68
- package/src/services/adapters/base.ts +0 -38
- package/src/services/adapters/chatCompletions.ts +0 -90
- package/src/services/adapters/responsesAPI.ts +0 -170
- package/src/services/browserMocks.ts +0 -66
- package/src/services/claude.ts +0 -2197
- package/src/services/customCommands.ts +0 -704
- package/src/services/fileFreshness.ts +0 -377
- package/src/services/gpt5ConnectionTest.ts +0 -340
- package/src/services/mcpClient.ts +0 -564
- package/src/services/mcpServerApproval.tsx +0 -50
- package/src/services/mentionProcessor.ts +0 -273
- package/src/services/modelAdapterFactory.ts +0 -69
- package/src/services/notifier.ts +0 -40
- package/src/services/oauth.ts +0 -357
- package/src/services/openai.ts +0 -1359
- package/src/services/responseStateManager.ts +0 -90
- package/src/services/sentry.ts +0 -3
- package/src/services/statsig.ts +0 -172
- package/src/services/statsigStorage.ts +0 -86
- package/src/services/systemReminder.ts +0 -507
- package/src/services/vcr.ts +0 -161
- package/src/test/testAdapters.ts +0 -96
- package/src/tools/ArchitectTool/ArchitectTool.tsx +0 -135
- package/src/tools/ArchitectTool/prompt.ts +0 -15
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +0 -576
- package/src/tools/BashTool/BashTool.tsx +0 -243
- package/src/tools/BashTool/BashToolResultMessage.tsx +0 -38
- package/src/tools/BashTool/OutputLine.tsx +0 -49
- package/src/tools/BashTool/prompt.ts +0 -174
- package/src/tools/BashTool/utils.ts +0 -56
- package/src/tools/FileEditTool/FileEditTool.tsx +0 -319
- package/src/tools/FileEditTool/prompt.ts +0 -51
- package/src/tools/FileEditTool/utils.ts +0 -58
- package/src/tools/FileReadTool/FileReadTool.tsx +0 -404
- package/src/tools/FileReadTool/prompt.ts +0 -7
- package/src/tools/FileWriteTool/FileWriteTool.tsx +0 -301
- package/src/tools/FileWriteTool/prompt.ts +0 -10
- package/src/tools/GlobTool/GlobTool.tsx +0 -119
- package/src/tools/GlobTool/prompt.ts +0 -8
- package/src/tools/GrepTool/GrepTool.tsx +0 -147
- package/src/tools/GrepTool/prompt.ts +0 -11
- package/src/tools/MCPTool/MCPTool.tsx +0 -107
- package/src/tools/MCPTool/prompt.ts +0 -3
- package/src/tools/MemoryReadTool/MemoryReadTool.tsx +0 -127
- package/src/tools/MemoryReadTool/prompt.ts +0 -3
- package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +0 -89
- package/src/tools/MemoryWriteTool/prompt.ts +0 -3
- package/src/tools/MultiEditTool/MultiEditTool.tsx +0 -388
- package/src/tools/MultiEditTool/prompt.ts +0 -45
- package/src/tools/NotebookEditTool/NotebookEditTool.tsx +0 -298
- package/src/tools/NotebookEditTool/prompt.ts +0 -3
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +0 -258
- package/src/tools/NotebookReadTool/prompt.ts +0 -3
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +0 -107
- package/src/tools/StickerRequestTool/prompt.ts +0 -19
- package/src/tools/TaskTool/TaskTool.tsx +0 -438
- package/src/tools/TaskTool/constants.ts +0 -1
- package/src/tools/TaskTool/prompt.ts +0 -92
- package/src/tools/ThinkTool/ThinkTool.tsx +0 -54
- package/src/tools/ThinkTool/prompt.ts +0 -12
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +0 -313
- package/src/tools/TodoWriteTool/prompt.ts +0 -63
- package/src/tools/URLFetcherTool/URLFetcherTool.tsx +0 -178
- package/src/tools/URLFetcherTool/cache.ts +0 -55
- package/src/tools/URLFetcherTool/htmlToMarkdown.ts +0 -55
- package/src/tools/URLFetcherTool/prompt.ts +0 -17
- package/src/tools/WebSearchTool/WebSearchTool.tsx +0 -103
- package/src/tools/WebSearchTool/prompt.ts +0 -13
- package/src/tools/WebSearchTool/searchProviders.ts +0 -66
- package/src/tools/lsTool/lsTool.tsx +0 -272
- package/src/tools/lsTool/prompt.ts +0 -2
- package/src/tools.ts +0 -67
- package/src/types/PermissionMode.ts +0 -120
- package/src/types/RequestContext.ts +0 -72
- package/src/types/common.d.ts +0 -2
- package/src/types/conversation.ts +0 -51
- package/src/types/logs.ts +0 -58
- package/src/types/modelCapabilities.ts +0 -64
- package/src/types/notebook.ts +0 -87
- package/src/utils/Cursor.ts +0 -436
- package/src/utils/PersistentShell.ts +0 -552
- package/src/utils/advancedFuzzyMatcher.ts +0 -290
- package/src/utils/agentLoader.ts +0 -278
- package/src/utils/agentStorage.ts +0 -97
- package/src/utils/array.ts +0 -3
- package/src/utils/ask.tsx +0 -99
- package/src/utils/auth.ts +0 -13
- package/src/utils/autoCompactCore.ts +0 -223
- package/src/utils/autoUpdater.ts +0 -458
- package/src/utils/betas.ts +0 -20
- package/src/utils/browser.ts +0 -14
- package/src/utils/cleanup.ts +0 -72
- package/src/utils/commands.ts +0 -261
- package/src/utils/commonUnixCommands.ts +0 -161
- package/src/utils/config.ts +0 -945
- package/src/utils/conversationRecovery.ts +0 -55
- package/src/utils/debugLogger.ts +0 -1235
- package/src/utils/diff.ts +0 -42
- package/src/utils/env.ts +0 -57
- package/src/utils/errors.ts +0 -21
- package/src/utils/exampleCommands.ts +0 -109
- package/src/utils/execFileNoThrow.ts +0 -51
- package/src/utils/expertChatStorage.ts +0 -136
- package/src/utils/file.ts +0 -405
- package/src/utils/fileRecoveryCore.ts +0 -71
- package/src/utils/format.tsx +0 -44
- package/src/utils/fuzzyMatcher.ts +0 -328
- package/src/utils/generators.ts +0 -62
- package/src/utils/git.ts +0 -92
- package/src/utils/globalLogger.ts +0 -77
- package/src/utils/http.ts +0 -10
- package/src/utils/imagePaste.ts +0 -38
- package/src/utils/json.ts +0 -13
- package/src/utils/log.ts +0 -382
- package/src/utils/markdown.ts +0 -213
- package/src/utils/messageContextManager.ts +0 -294
- package/src/utils/messages.tsx +0 -945
- package/src/utils/model.ts +0 -914
- package/src/utils/permissions/filesystem.ts +0 -127
- package/src/utils/responseState.ts +0 -23
- package/src/utils/ripgrep.ts +0 -167
- package/src/utils/secureFile.ts +0 -564
- package/src/utils/sessionState.ts +0 -49
- package/src/utils/state.ts +0 -25
- package/src/utils/style.ts +0 -29
- package/src/utils/terminal.ts +0 -50
- package/src/utils/theme.ts +0 -127
- package/src/utils/thinking.ts +0 -144
- package/src/utils/todoStorage.ts +0 -431
- package/src/utils/tokens.ts +0 -43
- package/src/utils/toolExecutionController.ts +0 -163
- package/src/utils/unaryLogging.ts +0 -26
- package/src/utils/user.ts +0 -37
- package/src/utils/validate.ts +0 -165
package/src/utils/secureFile.ts
DELETED
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, renameSync } from 'node:fs'
|
|
2
|
-
import { join, dirname, normalize, resolve, extname, relative, isAbsolute } from 'node:path'
|
|
3
|
-
import { homedir } from 'node:os'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 安全文件系统操作服务
|
|
7
|
-
* 解决文件系统操作中缺少适当验证和错误处理的问题
|
|
8
|
-
*/
|
|
9
|
-
export class SecureFileService {
|
|
10
|
-
private static instance: SecureFileService
|
|
11
|
-
private allowedBasePaths: Set<string>
|
|
12
|
-
private maxFileSize: number
|
|
13
|
-
private allowedExtensions: Set<string>
|
|
14
|
-
|
|
15
|
-
private constructor() {
|
|
16
|
-
// 允许的基础路径
|
|
17
|
-
this.allowedBasePaths = new Set([
|
|
18
|
-
process.cwd(),
|
|
19
|
-
homedir(),
|
|
20
|
-
'/tmp',
|
|
21
|
-
'/var/tmp'
|
|
22
|
-
])
|
|
23
|
-
|
|
24
|
-
// 默认最大文件大小 (10MB)
|
|
25
|
-
this.maxFileSize = 10 * 1024 * 1024
|
|
26
|
-
|
|
27
|
-
// 允许的文件扩展名
|
|
28
|
-
this.allowedExtensions = new Set([
|
|
29
|
-
'.txt', '.md', '.json', '.js', '.ts', '.tsx', '.jsx',
|
|
30
|
-
'.yaml', '.yml', '.toml', '.ini', '.env', '.log',
|
|
31
|
-
'.html', '.css', '.scss', '.less', '.xml', '.csv',
|
|
32
|
-
'.py', '.go', '.rs', '.java', '.cpp', '.c', '.h',
|
|
33
|
-
'.sh', '.bash', '.zsh', '.fish', '.ps1', '.bat',
|
|
34
|
-
'.dockerfile', '.gitignore', '.npmignore', '.eslintignore'
|
|
35
|
-
])
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public static getInstance(): SecureFileService {
|
|
39
|
-
if (!SecureFileService.instance) {
|
|
40
|
-
SecureFileService.instance = new SecureFileService()
|
|
41
|
-
}
|
|
42
|
-
return SecureFileService.instance
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* 验证文件路径是否安全
|
|
47
|
-
* @param filePath 文件路径
|
|
48
|
-
* @returns 验证结果
|
|
49
|
-
*/
|
|
50
|
-
public validateFilePath(filePath: string): { isValid: boolean; normalizedPath: string; error?: string } {
|
|
51
|
-
try {
|
|
52
|
-
// 规范化路径
|
|
53
|
-
const normalizedPath = normalize(filePath)
|
|
54
|
-
|
|
55
|
-
// 检查路径长度
|
|
56
|
-
if (normalizedPath.length > 4096) {
|
|
57
|
-
return {
|
|
58
|
-
isValid: false,
|
|
59
|
-
normalizedPath,
|
|
60
|
-
error: 'Path too long (max 4096 characters)'
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 检查是否包含路径遍历字符
|
|
65
|
-
if (normalizedPath.includes('..') || normalizedPath.includes('~')) {
|
|
66
|
-
return {
|
|
67
|
-
isValid: false,
|
|
68
|
-
normalizedPath,
|
|
69
|
-
error: 'Path contains traversal characters'
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 检查是否包含可疑的字符序列
|
|
74
|
-
const suspiciousPatterns = [
|
|
75
|
-
/\.\./, // 父目录
|
|
76
|
-
/~/, // 用户目录
|
|
77
|
-
/\$\{/, // 环境变量
|
|
78
|
-
/`/, // 命令执行
|
|
79
|
-
/\|/, // 管道符
|
|
80
|
-
/;/, // 命令分隔符
|
|
81
|
-
/&/, // 后台执行
|
|
82
|
-
/>/, // 输出重定向
|
|
83
|
-
/</, // 输入重定向
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
for (const pattern of suspiciousPatterns) {
|
|
87
|
-
if (pattern.test(normalizedPath)) {
|
|
88
|
-
return {
|
|
89
|
-
isValid: false,
|
|
90
|
-
normalizedPath,
|
|
91
|
-
error: `Path contains suspicious pattern: ${pattern}`
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 解析为绝对路径
|
|
97
|
-
const absolutePath = resolve(normalizedPath)
|
|
98
|
-
|
|
99
|
-
// 检查是否在允许的基础路径中
|
|
100
|
-
const isInAllowedPath = Array.from(this.allowedBasePaths).some(basePath => {
|
|
101
|
-
const base = resolve(basePath)
|
|
102
|
-
const rel = relative(base, absolutePath)
|
|
103
|
-
if (!rel || rel === '') return true
|
|
104
|
-
if (rel.startsWith('..')) return false
|
|
105
|
-
if (isAbsolute(rel)) return false
|
|
106
|
-
return true
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
if (!isInAllowedPath) {
|
|
110
|
-
return {
|
|
111
|
-
isValid: false,
|
|
112
|
-
normalizedPath,
|
|
113
|
-
error: 'Path is outside allowed directories'
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return { isValid: true, normalizedPath: absolutePath }
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
isValid: false,
|
|
121
|
-
normalizedPath: filePath,
|
|
122
|
-
error: `Path validation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 安全地检查文件是否存在
|
|
129
|
-
* @param filePath 文件路径
|
|
130
|
-
* @returns 文件是否存在
|
|
131
|
-
*/
|
|
132
|
-
public safeExists(filePath: string): boolean {
|
|
133
|
-
const validation = this.validateFilePath(filePath)
|
|
134
|
-
if (!validation.isValid) {
|
|
135
|
-
return false
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
return existsSync(validation.normalizedPath)
|
|
140
|
-
} catch (error) {
|
|
141
|
-
return false
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 安全地读取文件
|
|
147
|
-
* @param filePath 文件路径
|
|
148
|
-
* @param options 读取选项
|
|
149
|
-
* @returns 读取结果
|
|
150
|
-
*/
|
|
151
|
-
public safeReadFile(
|
|
152
|
-
filePath: string,
|
|
153
|
-
options: {
|
|
154
|
-
encoding?: BufferEncoding;
|
|
155
|
-
maxFileSize?: number;
|
|
156
|
-
allowedExtensions?: string[];
|
|
157
|
-
checkFileExtension?: boolean;
|
|
158
|
-
} = {}
|
|
159
|
-
): { success: boolean; content?: string | Buffer; error?: string; stats?: any } {
|
|
160
|
-
const validation = this.validateFilePath(filePath)
|
|
161
|
-
if (!validation.isValid) {
|
|
162
|
-
return { success: false, error: validation.error }
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const normalizedPath = validation.normalizedPath
|
|
167
|
-
|
|
168
|
-
// 检查文件扩展名(如果启用)
|
|
169
|
-
if (options.checkFileExtension !== false) {
|
|
170
|
-
const ext = extname(normalizedPath).toLowerCase()
|
|
171
|
-
const allowedExts = options.allowedExtensions ||
|
|
172
|
-
Array.from(this.allowedExtensions)
|
|
173
|
-
|
|
174
|
-
if (allowedExts.length > 0 && !allowedExts.includes(ext)) {
|
|
175
|
-
return {
|
|
176
|
-
success: false,
|
|
177
|
-
error: `File extension '${ext}' is not allowed`
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 检查文件是否存在
|
|
183
|
-
if (!existsSync(normalizedPath)) {
|
|
184
|
-
return { success: false, error: 'File does not exist' }
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// 获取文件信息
|
|
188
|
-
const stats = statSync(normalizedPath)
|
|
189
|
-
const maxSize = options.maxFileSize || this.maxFileSize
|
|
190
|
-
|
|
191
|
-
// 检查文件大小
|
|
192
|
-
if (stats.size > maxSize) {
|
|
193
|
-
return {
|
|
194
|
-
success: false,
|
|
195
|
-
error: `File too large (${stats.size} bytes, max ${maxSize} bytes)`
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// 检查文件类型
|
|
200
|
-
if (!stats.isFile()) {
|
|
201
|
-
return { success: false, error: 'Path is not a file' }
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// 检查文件权限
|
|
205
|
-
if ((stats.mode & parseInt('400', 8)) === 0) { // 检查读权限
|
|
206
|
-
return { success: false, error: 'No read permission' }
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// 读取文件内容
|
|
210
|
-
const content = readFileSync(normalizedPath, {
|
|
211
|
-
encoding: options.encoding || 'utf8'
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
success: true,
|
|
216
|
-
content,
|
|
217
|
-
stats: {
|
|
218
|
-
size: stats.size,
|
|
219
|
-
mtime: stats.mtime,
|
|
220
|
-
atime: stats.atime,
|
|
221
|
-
mode: stats.mode
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} catch (error) {
|
|
225
|
-
return {
|
|
226
|
-
success: false,
|
|
227
|
-
error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* 安全地写入文件
|
|
234
|
-
* @param filePath 文件路径
|
|
235
|
-
* @param content 文件内容
|
|
236
|
-
* @param options 写入选项
|
|
237
|
-
* @returns 写入结果
|
|
238
|
-
*/
|
|
239
|
-
public safeWriteFile(
|
|
240
|
-
filePath: string,
|
|
241
|
-
content: string | Buffer,
|
|
242
|
-
options: {
|
|
243
|
-
encoding?: BufferEncoding;
|
|
244
|
-
createDirectory?: boolean;
|
|
245
|
-
atomic?: boolean;
|
|
246
|
-
mode?: number;
|
|
247
|
-
allowedExtensions?: string[];
|
|
248
|
-
checkFileExtension?: boolean;
|
|
249
|
-
maxSize?: number;
|
|
250
|
-
} = {}
|
|
251
|
-
): { success: boolean; error?: string } {
|
|
252
|
-
const validation = this.validateFilePath(filePath)
|
|
253
|
-
if (!validation.isValid) {
|
|
254
|
-
return { success: false, error: validation.error }
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
const normalizedPath = validation.normalizedPath
|
|
259
|
-
|
|
260
|
-
// 检查文件扩展名(如果启用)
|
|
261
|
-
if (options.checkFileExtension !== false) {
|
|
262
|
-
const ext = extname(normalizedPath).toLowerCase()
|
|
263
|
-
const allowedExts = options.allowedExtensions ||
|
|
264
|
-
Array.from(this.allowedExtensions)
|
|
265
|
-
|
|
266
|
-
if (allowedExts.length > 0 && !allowedExts.includes(ext)) {
|
|
267
|
-
return {
|
|
268
|
-
success: false,
|
|
269
|
-
error: `File extension '${ext}' is not allowed`
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// 检查内容大小
|
|
275
|
-
const contentSize = typeof content === 'string' ?
|
|
276
|
-
Buffer.byteLength(content, options.encoding as BufferEncoding || 'utf8') :
|
|
277
|
-
content.length
|
|
278
|
-
|
|
279
|
-
const maxSize = options.maxSize || this.maxFileSize
|
|
280
|
-
if (contentSize > maxSize) {
|
|
281
|
-
return {
|
|
282
|
-
success: false,
|
|
283
|
-
error: `Content too large (${contentSize} bytes, max ${maxSize} bytes)`
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 创建目录(如果需要)
|
|
288
|
-
if (options.createDirectory) {
|
|
289
|
-
const dir = dirname(normalizedPath)
|
|
290
|
-
if (!existsSync(dir)) {
|
|
291
|
-
mkdirSync(dir, { recursive: true, mode: 0o755 })
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// 原子写入(如果启用)
|
|
296
|
-
if (options.atomic) {
|
|
297
|
-
const tempPath = `${normalizedPath}.tmp.${Date.now()}`
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
// 写入临时文件
|
|
301
|
-
writeFileSync(tempPath, content, {
|
|
302
|
-
encoding: options.encoding as BufferEncoding || 'utf8',
|
|
303
|
-
mode: options.mode || 0o644
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
// 重命名为目标文件
|
|
307
|
-
renameSync(tempPath, normalizedPath)
|
|
308
|
-
} catch (renameError) {
|
|
309
|
-
// 清理临时文件
|
|
310
|
-
try {
|
|
311
|
-
if (existsSync(tempPath)) {
|
|
312
|
-
unlinkSync(tempPath)
|
|
313
|
-
}
|
|
314
|
-
} catch {
|
|
315
|
-
// 忽略清理错误
|
|
316
|
-
}
|
|
317
|
-
throw renameError
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
// 直接写入
|
|
321
|
-
writeFileSync(normalizedPath, content, {
|
|
322
|
-
encoding: options.encoding as BufferEncoding || 'utf8',
|
|
323
|
-
mode: options.mode || 0o644
|
|
324
|
-
})
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return { success: true }
|
|
328
|
-
} catch (error) {
|
|
329
|
-
return {
|
|
330
|
-
success: false,
|
|
331
|
-
error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}`
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* 安全地删除文件
|
|
338
|
-
* @param filePath 文件路径
|
|
339
|
-
* @returns 删除结果
|
|
340
|
-
*/
|
|
341
|
-
public safeDeleteFile(filePath: string): { success: boolean; error?: string } {
|
|
342
|
-
const validation = this.validateFilePath(filePath)
|
|
343
|
-
if (!validation.isValid) {
|
|
344
|
-
return { success: false, error: validation.error }
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
try {
|
|
348
|
-
const normalizedPath = validation.normalizedPath
|
|
349
|
-
|
|
350
|
-
// 检查文件是否存在
|
|
351
|
-
if (!existsSync(normalizedPath)) {
|
|
352
|
-
return { success: false, error: 'File does not exist' }
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// 检查文件类型
|
|
356
|
-
const stats = statSync(normalizedPath)
|
|
357
|
-
if (!stats.isFile()) {
|
|
358
|
-
return { success: false, error: 'Path is not a file' }
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// 检查写权限
|
|
362
|
-
if ((stats.mode & parseInt('200', 8)) === 0) {
|
|
363
|
-
return { success: false, error: 'No write permission' }
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// 安全删除
|
|
367
|
-
unlinkSync(normalizedPath)
|
|
368
|
-
return { success: true }
|
|
369
|
-
} catch (error) {
|
|
370
|
-
return {
|
|
371
|
-
success: false,
|
|
372
|
-
error: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}`
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* 安全地创建目录
|
|
379
|
-
* @param dirPath 目录路径
|
|
380
|
-
* @param mode 目录权限
|
|
381
|
-
* @returns 创建结果
|
|
382
|
-
*/
|
|
383
|
-
public safeCreateDirectory(dirPath: string, mode: number = 0o755): { success: boolean; error?: string } {
|
|
384
|
-
const validation = this.validateFilePath(dirPath)
|
|
385
|
-
if (!validation.isValid) {
|
|
386
|
-
return { success: false, error: validation.error }
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
try {
|
|
390
|
-
const normalizedPath = validation.normalizedPath
|
|
391
|
-
|
|
392
|
-
if (existsSync(normalizedPath)) {
|
|
393
|
-
const stats = statSync(normalizedPath)
|
|
394
|
-
if (!stats.isDirectory()) {
|
|
395
|
-
return { success: false, error: 'Path already exists and is not a directory' }
|
|
396
|
-
}
|
|
397
|
-
return { success: true }
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
mkdirSync(normalizedPath, { recursive: true, mode })
|
|
401
|
-
return { success: true }
|
|
402
|
-
} catch (error) {
|
|
403
|
-
return {
|
|
404
|
-
success: false,
|
|
405
|
-
error: `Failed to create directory: ${error instanceof Error ? error.message : String(error)}`
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* 安全地获取文件信息
|
|
412
|
-
* @param filePath 文件路径
|
|
413
|
-
* @returns 文件信息
|
|
414
|
-
*/
|
|
415
|
-
public safeGetFileInfo(filePath: string): {
|
|
416
|
-
success: boolean;
|
|
417
|
-
stats?: {
|
|
418
|
-
size: number;
|
|
419
|
-
isFile: boolean;
|
|
420
|
-
isDirectory: boolean;
|
|
421
|
-
mode: number;
|
|
422
|
-
atime: Date;
|
|
423
|
-
mtime: Date;
|
|
424
|
-
ctime: Date;
|
|
425
|
-
};
|
|
426
|
-
error?: string
|
|
427
|
-
} {
|
|
428
|
-
const validation = this.validateFilePath(filePath)
|
|
429
|
-
if (!validation.isValid) {
|
|
430
|
-
return { success: false, error: validation.error }
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
try {
|
|
434
|
-
const normalizedPath = validation.normalizedPath
|
|
435
|
-
|
|
436
|
-
if (!existsSync(normalizedPath)) {
|
|
437
|
-
return { success: false, error: 'File does not exist' }
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const stats = statSync(normalizedPath)
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
success: true,
|
|
444
|
-
stats: {
|
|
445
|
-
size: stats.size,
|
|
446
|
-
isFile: stats.isFile(),
|
|
447
|
-
isDirectory: stats.isDirectory(),
|
|
448
|
-
mode: stats.mode,
|
|
449
|
-
atime: stats.atime,
|
|
450
|
-
mtime: stats.mtime,
|
|
451
|
-
ctime: stats.ctime
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
} catch (error) {
|
|
455
|
-
return {
|
|
456
|
-
success: false,
|
|
457
|
-
error: `Failed to get file info: ${error instanceof Error ? error.message : String(error)}`
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* 添加允许的基础路径
|
|
464
|
-
* @param basePath 基础路径
|
|
465
|
-
*/
|
|
466
|
-
public addAllowedBasePath(basePath: string): { success: boolean; error?: string } {
|
|
467
|
-
try {
|
|
468
|
-
const normalized = normalize(resolve(basePath))
|
|
469
|
-
|
|
470
|
-
// 验证路径是否存在
|
|
471
|
-
if (!existsSync(normalized)) {
|
|
472
|
-
return { success: false, error: 'Base path does not exist' }
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
this.allowedBasePaths.add(normalized)
|
|
476
|
-
return { success: true }
|
|
477
|
-
} catch (error) {
|
|
478
|
-
return {
|
|
479
|
-
success: false,
|
|
480
|
-
error: `Failed to add base path: ${error instanceof Error ? error.message : String(error)}`
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* 设置最大文件大小
|
|
487
|
-
* @param maxSize 最大文件大小(字节)
|
|
488
|
-
*/
|
|
489
|
-
public setMaxFileSize(maxSize: number): void {
|
|
490
|
-
this.maxFileSize = maxSize
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* 添加允许的文件扩展名
|
|
495
|
-
* @param extensions 文件扩展名数组
|
|
496
|
-
*/
|
|
497
|
-
public addAllowedExtensions(extensions: string[]): void {
|
|
498
|
-
extensions.forEach(ext => {
|
|
499
|
-
if (!ext.startsWith('.')) {
|
|
500
|
-
ext = '.' + ext
|
|
501
|
-
}
|
|
502
|
-
this.allowedExtensions.add(ext.toLowerCase())
|
|
503
|
-
})
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* 检查文件是否在允许的基础路径中
|
|
508
|
-
* @param filePath 文件路径
|
|
509
|
-
* @returns 是否允许
|
|
510
|
-
*/
|
|
511
|
-
public isPathAllowed(filePath: string): boolean {
|
|
512
|
-
const validation = this.validateFilePath(filePath)
|
|
513
|
-
return validation.isValid
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* 验证文件名安全性
|
|
518
|
-
* @param filename 文件名
|
|
519
|
-
* @returns 验证结果
|
|
520
|
-
*/
|
|
521
|
-
public validateFileName(filename: string): { isValid: boolean; error?: string } {
|
|
522
|
-
// 检查文件名长度
|
|
523
|
-
if (filename.length === 0) {
|
|
524
|
-
return { isValid: false, error: 'Filename cannot be empty' }
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (filename.length > 255) {
|
|
528
|
-
return { isValid: false, error: 'Filename too long (max 255 characters)' }
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// 检查文件名字符
|
|
532
|
-
const invalidChars = /[<>:"/\\|?*\x00-\x1F]/
|
|
533
|
-
if (invalidChars.test(filename)) {
|
|
534
|
-
return { isValid: false, error: 'Filename contains invalid characters' }
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// 检查保留文件名
|
|
538
|
-
const reservedNames = [
|
|
539
|
-
'CON', 'PRN', 'AUX', 'NUL',
|
|
540
|
-
'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',
|
|
541
|
-
'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'
|
|
542
|
-
]
|
|
543
|
-
|
|
544
|
-
const baseName = filename.split('.')[0].toUpperCase()
|
|
545
|
-
if (reservedNames.includes(baseName)) {
|
|
546
|
-
return { isValid: false, error: 'Filename is reserved' }
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// 检查是否以点开头或结尾
|
|
550
|
-
if (filename.startsWith('.') || filename.endsWith('.')) {
|
|
551
|
-
return { isValid: false, error: 'Filename cannot start or end with a dot' }
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// 检查是否以空格开头或结尾
|
|
555
|
-
if (filename.startsWith(' ') || filename.endsWith(' ')) {
|
|
556
|
-
return { isValid: false, error: 'Filename cannot start or end with spaces' }
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
return { isValid: true }
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// 导出单例实例
|
|
564
|
-
export const secureFileService = SecureFileService.getInstance()
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { logEvent } from '../services/statsig'
|
|
2
|
-
type SessionState = {
|
|
3
|
-
modelErrors: Record<string, unknown>
|
|
4
|
-
currentError: string | null
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const isDebug =
|
|
8
|
-
process.argv.includes('--debug') ||
|
|
9
|
-
process.argv.includes('-d') ||
|
|
10
|
-
process.env.DEBUG === 'true'
|
|
11
|
-
|
|
12
|
-
const sessionState: SessionState = {
|
|
13
|
-
modelErrors: {},
|
|
14
|
-
currentError: null,
|
|
15
|
-
} as const
|
|
16
|
-
|
|
17
|
-
function setSessionState<K extends keyof SessionState>(
|
|
18
|
-
key: K,
|
|
19
|
-
value: SessionState[K],
|
|
20
|
-
): void
|
|
21
|
-
function setSessionState(partialState: Partial<SessionState>): void
|
|
22
|
-
function setSessionState(
|
|
23
|
-
keyOrState: keyof SessionState | Partial<SessionState>,
|
|
24
|
-
value?: any,
|
|
25
|
-
): void {
|
|
26
|
-
if (typeof keyOrState === 'string') {
|
|
27
|
-
logEvent('session_state_set', {
|
|
28
|
-
key: keyOrState,
|
|
29
|
-
value: JSON.stringify(value),
|
|
30
|
-
})
|
|
31
|
-
sessionState[keyOrState] = value
|
|
32
|
-
} else {
|
|
33
|
-
logEvent('session_state_set', {
|
|
34
|
-
key: 'partial',
|
|
35
|
-
value: JSON.stringify(keyOrState),
|
|
36
|
-
})
|
|
37
|
-
Object.assign(sessionState, keyOrState)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getSessionState(): SessionState
|
|
42
|
-
function getSessionState<K extends keyof SessionState>(key: K): SessionState[K]
|
|
43
|
-
function getSessionState<K extends keyof SessionState>(key?: K) {
|
|
44
|
-
return key === undefined ? sessionState : sessionState[key]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export type { SessionState }
|
|
48
|
-
export { setSessionState, getSessionState }
|
|
49
|
-
export default sessionState
|
package/src/utils/state.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { cwd } from 'process'
|
|
2
|
-
import { PersistentShell } from './PersistentShell'
|
|
3
|
-
|
|
4
|
-
// DO NOT ADD MORE STATE HERE OR BORIS WILL CURSE YOU
|
|
5
|
-
const STATE: {
|
|
6
|
-
originalCwd: string
|
|
7
|
-
} = {
|
|
8
|
-
originalCwd: cwd(),
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function setCwd(cwd: string): Promise<void> {
|
|
12
|
-
await PersistentShell.getInstance().setCwd(cwd)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function setOriginalCwd(cwd: string): void {
|
|
16
|
-
STATE.originalCwd = cwd
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getOriginalCwd(): string {
|
|
20
|
-
return STATE.originalCwd
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function getCwd(): string {
|
|
24
|
-
return PersistentShell.getInstance().pwd()
|
|
25
|
-
}
|
package/src/utils/style.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'fs'
|
|
2
|
-
import { join, parse, dirname } from 'path'
|
|
3
|
-
import { memoize } from 'lodash-es'
|
|
4
|
-
import { getCwd } from './state'
|
|
5
|
-
import { PROJECT_FILE } from '../constants/product'
|
|
6
|
-
|
|
7
|
-
const STYLE_PROMPT =
|
|
8
|
-
'The codebase follows strict style guidelines shown below. All code changes must strictly adhere to these guidelines to maintain consistency and quality.'
|
|
9
|
-
|
|
10
|
-
export const getCodeStyle = memoize((): string => {
|
|
11
|
-
const styles: string[] = []
|
|
12
|
-
let currentDir = getCwd()
|
|
13
|
-
|
|
14
|
-
while (currentDir !== parse(currentDir).root) {
|
|
15
|
-
const stylePath = join(currentDir, PROJECT_FILE)
|
|
16
|
-
if (existsSync(stylePath)) {
|
|
17
|
-
styles.push(
|
|
18
|
-
`Contents of ${stylePath}:\n\n${readFileSync(stylePath, 'utf-8')}`,
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
currentDir = dirname(currentDir)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (styles.length === 0) {
|
|
25
|
-
return ''
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return `${STYLE_PROMPT}\n\n${styles.reverse().join('\n\n')}`
|
|
29
|
-
})
|