oricore 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -12
- package/README.zh-CN.md +18 -11
- package/dist/core/config.d.ts +20 -0
- package/dist/core/configValidation.d.ts +35 -0
- package/dist/core/history.d.ts +9 -0
- package/dist/session/session.d.ts +5 -1
- package/dist/utils/fileLock.d.ts +55 -0
- package/dist/utils/pdf-parser.d.ts +51 -0
- package/package.json +3 -2
- package/src/core/loop.ts +38 -0
- package/src/core/model.ts +12 -2
- package/src/mcp/mcp.ts +19 -2
- package/src/utils/safeParseJson.ts +14 -1
package/README.md
CHANGED
|
@@ -74,11 +74,11 @@ const engine = createEngine({
|
|
|
74
74
|
|
|
75
75
|
// 2. Initialize with model and API key
|
|
76
76
|
await engine.initialize({
|
|
77
|
-
model: '
|
|
77
|
+
model: 'openai/gpt-5.2-codex',
|
|
78
78
|
provider: {
|
|
79
|
-
|
|
79
|
+
openai: {
|
|
80
80
|
apiKey: 'your-api-key',
|
|
81
|
-
baseURL: 'https://
|
|
81
|
+
baseURL: 'https://api.openai.com/v1',
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
84
|
});
|
|
@@ -142,19 +142,19 @@ OriCore includes a comprehensive set of tools:
|
|
|
142
142
|
|
|
143
143
|
```typescript
|
|
144
144
|
await engine.initialize({
|
|
145
|
-
model: '
|
|
146
|
-
planModel: '
|
|
145
|
+
model: 'openai/gpt-5.2-codex',
|
|
146
|
+
planModel: 'openai/gpt-5.2-codex',
|
|
147
147
|
approvalMode: 'autoEdit',
|
|
148
|
-
language: '
|
|
148
|
+
language: 'en',
|
|
149
149
|
tools: {
|
|
150
150
|
read: true,
|
|
151
151
|
write: true,
|
|
152
152
|
bash: true,
|
|
153
153
|
},
|
|
154
154
|
provider: {
|
|
155
|
-
|
|
155
|
+
openai: {
|
|
156
156
|
apiKey: 'your-api-key',
|
|
157
|
-
baseURL: 'https://
|
|
157
|
+
baseURL: 'https://api.openai.com/v1',
|
|
158
158
|
},
|
|
159
159
|
},
|
|
160
160
|
});
|
|
@@ -164,11 +164,11 @@ await engine.initialize({
|
|
|
164
164
|
|
|
165
165
|
| Provider | Model Example | API Base URL |
|
|
166
166
|
|----------|---------------|--------------|
|
|
167
|
-
|
|
|
167
|
+
| OpenAI | `openai/gpt-5.2-codex` | `https://api.openai.com/v1` |
|
|
168
|
+
| Anthropic | `anthropic/claude-opus-4-5` | `https://api.anthropic.com` |
|
|
169
|
+
| Google | `google/gemini-3-flash-preview` | `https://generativelanguage.googleapis.com` |
|
|
168
170
|
| DeepSeek | `deepseek/deepseek-chat` | `https://api.deepseek.com` |
|
|
169
|
-
|
|
|
170
|
-
| Anthropic | `anthropic/claude-sonnet-4` | `https://api.anthropic.com` |
|
|
171
|
-
| Google | `google/gemini-2.5-flash` | `https://generativelanguage.googleapis.com` |
|
|
171
|
+
| Zhipu AI | `zhipuai/glm-4.7` | `https://open.bigmodel.cn/api/paas/v4` |
|
|
172
172
|
|
|
173
173
|
See [USAGE.md](./USAGE.md) for more configuration options.
|
|
174
174
|
|
|
@@ -190,6 +190,13 @@ oricore/
|
|
|
190
190
|
└── dist/ # Compiled output
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
+
## Statement
|
|
194
|
+
|
|
195
|
+
This project references the core architecture of the following excellent project:
|
|
196
|
+
- **[neovate-code](https://github.com/neovateai/neovate-code)** - Core AI engine architecture
|
|
197
|
+
|
|
198
|
+
OriCore has been refactored and streamlined on this foundation, removing UI, CLI, and other peripheral features to focus on providing a lightweight, standalone AI engine library that can be easily integrated into any project.
|
|
199
|
+
|
|
193
200
|
## License
|
|
194
201
|
|
|
195
202
|
MIT © [lyw405](https://github.com/lyw405)
|
package/README.zh-CN.md
CHANGED
|
@@ -74,11 +74,11 @@ const engine = createEngine({
|
|
|
74
74
|
|
|
75
75
|
// 2. 初始化模型和 API Key
|
|
76
76
|
await engine.initialize({
|
|
77
|
-
model: '
|
|
77
|
+
model: 'openai/gpt-5.2-codex',
|
|
78
78
|
provider: {
|
|
79
|
-
|
|
79
|
+
openai: {
|
|
80
80
|
apiKey: 'your-api-key',
|
|
81
|
-
baseURL: 'https://
|
|
81
|
+
baseURL: 'https://api.openai.com/v1',
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
84
|
});
|
|
@@ -142,8 +142,8 @@ OriCore 包含一套完整的工具:
|
|
|
142
142
|
|
|
143
143
|
```typescript
|
|
144
144
|
await engine.initialize({
|
|
145
|
-
model: '
|
|
146
|
-
planModel: '
|
|
145
|
+
model: 'openai/gpt-5.2-codex',
|
|
146
|
+
planModel: 'openai/gpt-5.2-codex',
|
|
147
147
|
approvalMode: 'autoEdit',
|
|
148
148
|
language: 'zh-CN',
|
|
149
149
|
tools: {
|
|
@@ -152,9 +152,9 @@ await engine.initialize({
|
|
|
152
152
|
bash: true,
|
|
153
153
|
},
|
|
154
154
|
provider: {
|
|
155
|
-
|
|
155
|
+
openai: {
|
|
156
156
|
apiKey: 'your-api-key',
|
|
157
|
-
baseURL: 'https://
|
|
157
|
+
baseURL: 'https://api.openai.com/v1',
|
|
158
158
|
},
|
|
159
159
|
},
|
|
160
160
|
});
|
|
@@ -164,11 +164,11 @@ await engine.initialize({
|
|
|
164
164
|
|
|
165
165
|
| 提供商 | 模型示例 | API 地址 |
|
|
166
166
|
|----------|---------------|--------------|
|
|
167
|
-
|
|
|
167
|
+
| OpenAI | `openai/gpt-5.2-codex` | `https://api.openai.com/v1` |
|
|
168
|
+
| Anthropic | `anthropic/claude-opus-4-5` | `https://api.anthropic.com` |
|
|
169
|
+
| Google | `google/gemini-3-flash-preview` | `https://generativelanguage.googleapis.com` |
|
|
168
170
|
| DeepSeek | `deepseek/deepseek-chat` | `https://api.deepseek.com` |
|
|
169
|
-
|
|
|
170
|
-
| Anthropic | `anthropic/claude-sonnet-4` | `https://api.anthropic.com` |
|
|
171
|
-
| Google | `google/gemini-2.5-flash` | `https://generativelanguage.googleapis.com` |
|
|
171
|
+
| 智谱 AI | `zhipuai/glm-4.7` | `https://open.bigmodel.cn/api/paas/v4` |
|
|
172
172
|
|
|
173
173
|
更多配置选项请参阅 [USAGE.zh-CN.md](./USAGE.zh-CN.md)。
|
|
174
174
|
|
|
@@ -190,6 +190,13 @@ oricore/
|
|
|
190
190
|
└── dist/ # 编译输出
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
+
## 声明
|
|
194
|
+
|
|
195
|
+
本项目参考了以下优秀项目的核心架构:
|
|
196
|
+
- **[neovate-code](https://github.com/neovateai/neovate-code)** - 核心 AI 引擎架构
|
|
197
|
+
|
|
198
|
+
OriCore 在此基础上进行了重新封装和精简,移除了 UI、CLI 等周边功能,专注于提供一个轻量、独立的 AI 引擎库,可轻松集成到任何项目中。
|
|
199
|
+
|
|
193
200
|
## 许可证
|
|
194
201
|
|
|
195
202
|
MIT © [lyw405](https://github.com/lyw405)
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider } from '../core/model';
|
|
2
|
+
import { type ValidationResult } from './configValidation';
|
|
2
3
|
export type McpStdioServerConfig = {
|
|
3
4
|
type: 'stdio';
|
|
4
5
|
command: string;
|
|
@@ -93,8 +94,27 @@ export declare class ConfigManager {
|
|
|
93
94
|
argvConfig: Partial<Config>;
|
|
94
95
|
globalConfigPath: string;
|
|
95
96
|
projectConfigPath: string;
|
|
97
|
+
private validationEnabled;
|
|
96
98
|
constructor(cwd: string, productName: string, argvConfig: Partial<Config>);
|
|
97
99
|
get config(): Config;
|
|
100
|
+
/**
|
|
101
|
+
* Enable configuration validation
|
|
102
|
+
* When enabled, invalid configurations will throw errors
|
|
103
|
+
*/
|
|
104
|
+
enableValidation(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Disable configuration validation
|
|
107
|
+
*/
|
|
108
|
+
disableValidation(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Validate the current configuration
|
|
111
|
+
* Returns validation result without throwing
|
|
112
|
+
*/
|
|
113
|
+
validate(): ValidationResult;
|
|
114
|
+
/**
|
|
115
|
+
* Get validation errors as a formatted string
|
|
116
|
+
*/
|
|
117
|
+
getValidationErrors(): string;
|
|
98
118
|
removeConfig(global: boolean, key: string, values?: string[]): void;
|
|
99
119
|
addConfig(global: boolean, key: string, values: string[]): void;
|
|
100
120
|
getConfig(global: boolean, key: string): any;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Validation
|
|
3
|
+
* Runtime validation for engine configuration
|
|
4
|
+
*/
|
|
5
|
+
import type { Config } from './config';
|
|
6
|
+
/**
|
|
7
|
+
* Validation result
|
|
8
|
+
*/
|
|
9
|
+
export interface ValidationResult {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
errors: ValidationError[];
|
|
12
|
+
warnings: ValidationWarning[];
|
|
13
|
+
}
|
|
14
|
+
export interface ValidationError {
|
|
15
|
+
path: string;
|
|
16
|
+
message: string;
|
|
17
|
+
value?: any;
|
|
18
|
+
}
|
|
19
|
+
export interface ValidationWarning {
|
|
20
|
+
path: string;
|
|
21
|
+
message: string;
|
|
22
|
+
value?: any;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validate a configuration object
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateConfig(config: Partial<Config>): ValidationResult;
|
|
28
|
+
/**
|
|
29
|
+
* Format validation errors for display
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatValidationErrors(result: ValidationResult): string;
|
|
32
|
+
/**
|
|
33
|
+
* Assert that configuration is valid, throws if not
|
|
34
|
+
*/
|
|
35
|
+
export declare function assertValidConfig(config: Partial<Config>): void;
|
package/dist/core/history.d.ts
CHANGED
|
@@ -17,8 +17,17 @@ export declare class History {
|
|
|
17
17
|
compress(model: ModelInfo): Promise<{
|
|
18
18
|
compressed: boolean;
|
|
19
19
|
summary?: undefined;
|
|
20
|
+
fallback?: undefined;
|
|
21
|
+
error?: undefined;
|
|
20
22
|
} | {
|
|
21
23
|
compressed: boolean;
|
|
22
24
|
summary: string;
|
|
25
|
+
fallback: boolean;
|
|
26
|
+
error: string;
|
|
27
|
+
} | {
|
|
28
|
+
compressed: boolean;
|
|
29
|
+
summary: string;
|
|
30
|
+
fallback: boolean;
|
|
31
|
+
error?: undefined;
|
|
23
32
|
}>;
|
|
24
33
|
}
|
|
@@ -2,6 +2,7 @@ import type { ApprovalMode } from '../core/config';
|
|
|
2
2
|
import { History } from '../core/history';
|
|
3
3
|
import type { NormalizedMessage } from '../core/message';
|
|
4
4
|
import { Usage } from '../core/usage';
|
|
5
|
+
import type { ModeType } from '../modes/types';
|
|
5
6
|
export type SessionId = string;
|
|
6
7
|
export declare class Session {
|
|
7
8
|
id: SessionId;
|
|
@@ -31,11 +32,14 @@ export type SessionConfig = {
|
|
|
31
32
|
export declare class SessionConfigManager {
|
|
32
33
|
logPath: string;
|
|
33
34
|
config: SessionConfig;
|
|
35
|
+
mode: ModeType | null;
|
|
34
36
|
constructor(opts: {
|
|
35
37
|
logPath: string;
|
|
36
38
|
});
|
|
37
39
|
load(logPath: string): SessionConfig;
|
|
38
|
-
|
|
40
|
+
setMode(mode: ModeType): void;
|
|
41
|
+
getMode(): ModeType | null;
|
|
42
|
+
write(): Promise<void>;
|
|
39
43
|
}
|
|
40
44
|
export declare function filterMessages(messages: NormalizedMessage[]): NormalizedMessage[];
|
|
41
45
|
export declare function loadSessionMessages(opts: {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based lock for concurrent write safety
|
|
3
|
+
* Uses lock files with unique identifiers to prevent race conditions
|
|
4
|
+
*/
|
|
5
|
+
export declare class FileLock {
|
|
6
|
+
private lockFilePath;
|
|
7
|
+
private lockId;
|
|
8
|
+
private acquired;
|
|
9
|
+
constructor(filePath: string);
|
|
10
|
+
/**
|
|
11
|
+
* Attempt to acquire the lock
|
|
12
|
+
* @returns true if lock was acquired, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
acquire(): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Release the lock
|
|
17
|
+
* Only releases if this instance holds the lock (matching lockId)
|
|
18
|
+
*/
|
|
19
|
+
release(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Execute a callback while holding the lock
|
|
22
|
+
* Automatically acquires and releases the lock
|
|
23
|
+
*/
|
|
24
|
+
withLock<T>(callback: () => T | Promise<T>): Promise<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Read and parse the lock file
|
|
27
|
+
*/
|
|
28
|
+
private readLockFile;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a lock has expired (older than timeout)
|
|
31
|
+
*/
|
|
32
|
+
private isLockExpired;
|
|
33
|
+
/**
|
|
34
|
+
* Force release any existing lock (use with caution)
|
|
35
|
+
* This can be used to recover from stale locks
|
|
36
|
+
*/
|
|
37
|
+
forceRelease(): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Global lock registry to manage multiple locks
|
|
41
|
+
* Helps prevent deadlocks and track active locks
|
|
42
|
+
*/
|
|
43
|
+
declare class LockRegistry {
|
|
44
|
+
private locks;
|
|
45
|
+
/**
|
|
46
|
+
* Get or create a lock for a file path
|
|
47
|
+
*/
|
|
48
|
+
getLock(filePath: string): FileLock;
|
|
49
|
+
/**
|
|
50
|
+
* Release all locks (useful for cleanup)
|
|
51
|
+
*/
|
|
52
|
+
releaseAll(): void;
|
|
53
|
+
}
|
|
54
|
+
export declare const lockRegistry: LockRegistry;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Parser Utility
|
|
3
|
+
* Extracts text content from PDF files
|
|
4
|
+
*/
|
|
5
|
+
export interface ParseResult {
|
|
6
|
+
text: string;
|
|
7
|
+
pageCount: number;
|
|
8
|
+
metadata?: {
|
|
9
|
+
title?: string;
|
|
10
|
+
author?: string;
|
|
11
|
+
subject?: string;
|
|
12
|
+
keywords?: string;
|
|
13
|
+
creator?: string;
|
|
14
|
+
producer?: string;
|
|
15
|
+
creationDate?: string;
|
|
16
|
+
modificationDate?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface ParseOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Maximum number of pages to parse (0 = all pages)
|
|
22
|
+
* @default 0
|
|
23
|
+
*/
|
|
24
|
+
maxPages?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum text length per page (0 = no limit)
|
|
27
|
+
* @default 10000
|
|
28
|
+
*/
|
|
29
|
+
maxCharsPerPage?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Include metadata in the result
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
includeMetadata?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a PDF file and extract text content
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - Absolute path to the PDF file
|
|
40
|
+
* @param options - Parsing options
|
|
41
|
+
* @returns Parsed PDF content
|
|
42
|
+
*/
|
|
43
|
+
export declare function parsePDF(filePath: string, options?: ParseOptions): Promise<ParseResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if PDF parsing is available (pdf-parse is installed)
|
|
46
|
+
*/
|
|
47
|
+
export declare function isPDFParsingAvailable(): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Format PDF parse result for LLM consumption
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatPDFResult(result: ParseResult): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oricore",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "OriCore - A powerful AI engine with multi-modal support, tool calling, and extensible architecture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
"tar": "^7.5.2",
|
|
79
79
|
"turndown": "^7.2.2",
|
|
80
80
|
"ws": "^8.18.3",
|
|
81
|
-
"zod": "^4.1.13"
|
|
81
|
+
"zod": "^4.1.13",
|
|
82
|
+
"jsonrepair": "^3.10.0"
|
|
82
83
|
},
|
|
83
84
|
"devDependencies": {
|
|
84
85
|
"@eslint/js": "^9.39.2",
|
package/src/core/loop.ts
CHANGED
|
@@ -545,6 +545,41 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
|
|
|
545
545
|
input: Record<string, any>;
|
|
546
546
|
result: ToolResult;
|
|
547
547
|
}[] = [];
|
|
548
|
+
|
|
549
|
+
// Helper function to add denied results for unprocessed tools
|
|
550
|
+
const addDeniedResultsForRemainingTools = async () => {
|
|
551
|
+
const processedToolCallIds = new Set(
|
|
552
|
+
toolResults.map((tr) => tr.toolCallId),
|
|
553
|
+
);
|
|
554
|
+
for (const remainingToolCall of toolCalls) {
|
|
555
|
+
if (!processedToolCallIds.has(remainingToolCall.toolCallId)) {
|
|
556
|
+
const remainingToolUse: ToolUse = {
|
|
557
|
+
name: remainingToolCall.toolName,
|
|
558
|
+
params: safeParseJson(remainingToolCall.input),
|
|
559
|
+
callId: remainingToolCall.toolCallId,
|
|
560
|
+
};
|
|
561
|
+
let remainingToolResult: ToolResult = {
|
|
562
|
+
llmContent:
|
|
563
|
+
'Error: Tool execution was skipped due to previous tool denial.',
|
|
564
|
+
isError: true,
|
|
565
|
+
};
|
|
566
|
+
if (opts.onToolResult) {
|
|
567
|
+
remainingToolResult = await opts.onToolResult(
|
|
568
|
+
remainingToolUse,
|
|
569
|
+
remainingToolResult,
|
|
570
|
+
false,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
toolResults.push({
|
|
574
|
+
toolCallId: remainingToolCall.toolCallId,
|
|
575
|
+
toolName: remainingToolCall.toolName,
|
|
576
|
+
input: safeParseJson(remainingToolCall.input),
|
|
577
|
+
result: remainingToolResult,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
548
583
|
for (const toolCall of toolCalls) {
|
|
549
584
|
let toolUse: ToolUse = {
|
|
550
585
|
name: toolCall.toolName,
|
|
@@ -609,6 +644,9 @@ export async function runLoop(opts: RunLoopOpts): Promise<LoopResult> {
|
|
|
609
644
|
result: toolResult,
|
|
610
645
|
});
|
|
611
646
|
|
|
647
|
+
// Add denied results for remaining unprocessed tools
|
|
648
|
+
await addDeniedResultsForRemainingTools();
|
|
649
|
+
|
|
612
650
|
if (!denyReason) {
|
|
613
651
|
await history.addMessage({
|
|
614
652
|
role: 'tool',
|
package/src/core/model.ts
CHANGED
|
@@ -1812,8 +1812,18 @@ export const providers: ProvidersMap = {
|
|
|
1812
1812
|
doc: 'https://cerebras.ai/docs',
|
|
1813
1813
|
models: {
|
|
1814
1814
|
'zai-glm-4.6': models['glm-4.6'],
|
|
1815
|
-
'zai-glm-4.7':
|
|
1816
|
-
|
|
1815
|
+
'zai-glm-4.7': {
|
|
1816
|
+
...models['glm-4.7'],
|
|
1817
|
+
// ref: https://inference-docs.cerebras.ai/models/zai-glm-47
|
|
1818
|
+
// default use the context of free tier
|
|
1819
|
+
limit: { context: 64000, output: 40000 },
|
|
1820
|
+
},
|
|
1821
|
+
'gpt-oss-120b': {
|
|
1822
|
+
...models['gpt-oss-120b'],
|
|
1823
|
+
// ref: https://inference-docs.cerebras.ai/models/openai-oss
|
|
1824
|
+
// default use the context of free tier
|
|
1825
|
+
limit: { context: 65000, output: 32000 },
|
|
1826
|
+
},
|
|
1817
1827
|
},
|
|
1818
1828
|
createModel(name, provider) {
|
|
1819
1829
|
const apiKey = getProviderApiKey(provider);
|
package/src/mcp/mcp.ts
CHANGED
|
@@ -335,10 +335,27 @@ export class MCPManager {
|
|
|
335
335
|
'@ai-sdk/mcp/mcp-stdio'
|
|
336
336
|
);
|
|
337
337
|
|
|
338
|
+
// Windows: if command is npx/npm/yarn/pnpm/bun/bunx, use cmd.exe to execute, fix: spawn npx ENOENT error
|
|
339
|
+
const windowsShellCommands = [
|
|
340
|
+
'npx',
|
|
341
|
+
'npm',
|
|
342
|
+
'yarn',
|
|
343
|
+
'pnpm',
|
|
344
|
+
'bun',
|
|
345
|
+
'bunx',
|
|
346
|
+
];
|
|
347
|
+
let command = config.command;
|
|
348
|
+
let args = config.args;
|
|
349
|
+
const isWin = process.platform === 'win32';
|
|
350
|
+
if (isWin && windowsShellCommands.includes(command.toLowerCase())) {
|
|
351
|
+
args = ['/c', command, ...(args || [])];
|
|
352
|
+
command = 'cmd.exe';
|
|
353
|
+
}
|
|
354
|
+
|
|
338
355
|
return experimental_createMCPClient({
|
|
339
356
|
transport: new Experimental_StdioMCPTransport({
|
|
340
|
-
command
|
|
341
|
-
args
|
|
357
|
+
command,
|
|
358
|
+
args,
|
|
342
359
|
stderr: 'ignore',
|
|
343
360
|
env,
|
|
344
361
|
}),
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
import createDebug from 'debug';
|
|
2
|
+
import { jsonrepair } from 'jsonrepair';
|
|
3
|
+
|
|
4
|
+
const debug = createDebug('oricore:utils:safeParseJson');
|
|
5
|
+
|
|
1
6
|
export function safeParseJson(json: string) {
|
|
2
7
|
try {
|
|
3
8
|
return JSON.parse(json);
|
|
4
9
|
} catch (_error) {
|
|
5
|
-
|
|
10
|
+
// try to repair the json
|
|
11
|
+
try {
|
|
12
|
+
debug('safeParseJson failed, trying to repair', _error);
|
|
13
|
+
const repairedJson = jsonrepair(json);
|
|
14
|
+
return JSON.parse(repairedJson);
|
|
15
|
+
} catch (_repairError) {
|
|
16
|
+
debug('safeParseJson failed, repair failed', _repairError);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
6
19
|
}
|
|
7
20
|
}
|