chatccc 0.2.246 → 0.2.247
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 +2 -0
- package/config.sample.json +2 -1
- package/deepccc-agent/README.md +34 -34
- package/deepccc-agent/os-prompts/darwin.md +8 -8
- package/deepccc-agent/os-prompts/linux.md +8 -8
- package/deepccc-agent/os-prompts/win32.md +9 -9
- package/deepccc-agent/package-lock.json +2027 -2027
- package/deepccc-agent/package.json +65 -65
- package/deepccc-agent/src/__tests__/chat-session.test.ts +741 -741
- package/deepccc-agent/src/__tests__/config.test.ts +34 -34
- package/deepccc-agent/src/__tests__/permissions.test.ts +199 -199
- package/deepccc-agent/src/__tests__/privacy.test.ts +15 -15
- package/deepccc-agent/src/cli.ts +25 -25
- package/deepccc-agent/src/config.ts +101 -101
- package/deepccc-agent/src/index.ts +99 -99
- package/package.json +74 -74
- package/src/__tests__/builtin-chat-session.test.ts +532 -532
- package/src/__tests__/builtin-permissions.test.ts +219 -219
- package/src/__tests__/ccc-adapter.test.ts +194 -194
- package/src/__tests__/config-reload.test.ts +1 -1
- package/src/__tests__/config-utils.test.ts +40 -0
- package/src/__tests__/session-ccc-config.test.ts +21 -0
- package/src/__tests__/session.test.ts +369 -369
- package/src/__tests__/web-ui.test.ts +2 -0
- package/src/adapters/adapter-interface.ts +42 -42
- package/src/adapters/ccc-adapter.ts +150 -150
- package/src/config-utils.ts +37 -13
- package/src/config.ts +23 -13
- package/src/session.ts +2 -0
- package/src/web-ui.ts +33 -2
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
|
|
5
|
-
export type DeepCccProvider = "openai" | "anthropic";
|
|
6
|
-
|
|
7
|
-
export interface DeepCccConfig {
|
|
8
|
-
/** API protocol/provider. Defaults to OpenAI-compatible. */
|
|
9
|
-
provider: DeepCccProvider;
|
|
10
|
-
apiKey: string;
|
|
11
|
-
baseURL: string;
|
|
12
|
-
model: string;
|
|
13
|
-
/** Reasoning effort(none/minimal/low/medium/high/xhigh/max),留空不传 reasoning_effort */
|
|
14
|
-
effort: string;
|
|
15
|
-
/** 主对话是否使用流式请求;默认开启 */
|
|
16
|
-
streaming: boolean;
|
|
17
|
-
rawStreamLogs: {
|
|
18
|
-
enabled: boolean;
|
|
19
|
-
maxBytesPerTurn: number;
|
|
20
|
-
retentionDays: number;
|
|
21
|
-
keepCompleted: boolean;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const DEEPCCC_HOME = join(homedir(), ".deepccc");
|
|
26
|
-
export const RAW_STREAM_LOGS_DIR = join(DEEPCCC_HOME, "raw-stream-logs");
|
|
27
|
-
const CONFIG_PATH = join(DEEPCCC_HOME, "config.json");
|
|
28
|
-
|
|
29
|
-
const DEFAULT_CONFIG: DeepCccConfig = {
|
|
30
|
-
provider: "openai",
|
|
31
|
-
apiKey: "",
|
|
32
|
-
baseURL: "https://api.deepseek.com/v1",
|
|
33
|
-
model: "deepseek-v4-pro",
|
|
34
|
-
effort: "",
|
|
35
|
-
streaming: true,
|
|
36
|
-
rawStreamLogs: {
|
|
37
|
-
enabled: false,
|
|
38
|
-
maxBytesPerTurn: 1024 * 1024,
|
|
39
|
-
retentionDays: 7,
|
|
40
|
-
keepCompleted: false,
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
function readConfigFile(): Partial<DeepCccConfig> {
|
|
45
|
-
if (!existsSync(CONFIG_PATH)) return {};
|
|
46
|
-
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Partial<DeepCccConfig>;
|
|
47
|
-
return raw && typeof raw === "object" ? raw : {};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function env(name: string): string | undefined {
|
|
51
|
-
const value = process.env[name]?.trim();
|
|
52
|
-
return value ? value : undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function boolEnv(name: string): boolean | undefined {
|
|
56
|
-
const value = env(name)?.toLowerCase();
|
|
57
|
-
if (value === undefined) return undefined;
|
|
58
|
-
if (["1", "true", "yes", "on"].includes(value)) return true;
|
|
59
|
-
if (["0", "false", "no", "off"].includes(value)) return false;
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function numberEnv(name: string): number | undefined {
|
|
64
|
-
const value = Number(env(name));
|
|
65
|
-
return Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function normalizeDeepCccProvider(value: unknown): DeepCccProvider {
|
|
69
|
-
if (value === undefined || value === null || String(value).trim() === "") return "openai";
|
|
70
|
-
const normalized = String(value).trim().toLowerCase();
|
|
71
|
-
if (normalized === "openai" || normalized === "anthropic") return normalized;
|
|
72
|
-
throw new Error(`DEEPCCC_PROVIDER/provider must be "openai" or "anthropic", received: ${String(value)}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function loadConfig(): DeepCccConfig {
|
|
76
|
-
const file = readConfigFile();
|
|
77
|
-
const rawLogs: Partial<DeepCccConfig["rawStreamLogs"]> = file.rawStreamLogs && typeof file.rawStreamLogs === "object"
|
|
78
|
-
? file.rawStreamLogs
|
|
79
|
-
: {};
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
provider: normalizeDeepCccProvider(env("DEEPCCC_PROVIDER") ?? file.provider ?? DEFAULT_CONFIG.provider),
|
|
83
|
-
apiKey: env("DEEPCCC_API_KEY") ?? env("DEEPSEEK_API_KEY") ?? file.apiKey ?? DEFAULT_CONFIG.apiKey,
|
|
84
|
-
baseURL: env("DEEPCCC_BASE_URL") ?? env("DEEPSEEK_BASE_URL") ?? file.baseURL ?? DEFAULT_CONFIG.baseURL,
|
|
85
|
-
model: env("DEEPCCC_MODEL") ?? env("DEEPSEEK_MODEL") ?? file.model ?? DEFAULT_CONFIG.model,
|
|
86
|
-
effort: env("DEEPCCC_EFFORT") ?? env("DEEPSEEK_EFFORT") ?? file.effort ?? DEFAULT_CONFIG.effort,
|
|
87
|
-
streaming: boolEnv("DEEPCCC_STREAMING") ?? file.streaming ?? DEFAULT_CONFIG.streaming,
|
|
88
|
-
rawStreamLogs: {
|
|
89
|
-
enabled: boolEnv("DEEPCCC_RAW_STREAM_LOGS") ?? rawLogs.enabled ?? DEFAULT_CONFIG.rawStreamLogs.enabled,
|
|
90
|
-
maxBytesPerTurn: numberEnv("DEEPCCC_RAW_STREAM_MAX_BYTES") ?? rawLogs.maxBytesPerTurn ?? DEFAULT_CONFIG.rawStreamLogs.maxBytesPerTurn,
|
|
91
|
-
retentionDays: numberEnv("DEEPCCC_RAW_STREAM_RETENTION_DAYS") ?? rawLogs.retentionDays ?? DEFAULT_CONFIG.rawStreamLogs.retentionDays,
|
|
92
|
-
keepCompleted: boolEnv("DEEPCCC_RAW_STREAM_KEEP_COMPLETED") ?? rawLogs.keepCompleted ?? DEFAULT_CONFIG.rawStreamLogs.keepCompleted,
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function ensureConfigDir(): void {
|
|
98
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const config = loadConfig();
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
export type DeepCccProvider = "openai" | "anthropic";
|
|
6
|
+
|
|
7
|
+
export interface DeepCccConfig {
|
|
8
|
+
/** API protocol/provider. Defaults to OpenAI-compatible. */
|
|
9
|
+
provider: DeepCccProvider;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
baseURL: string;
|
|
12
|
+
model: string;
|
|
13
|
+
/** Reasoning effort(none/minimal/low/medium/high/xhigh/max),留空不传 reasoning_effort */
|
|
14
|
+
effort: string;
|
|
15
|
+
/** 主对话是否使用流式请求;默认开启 */
|
|
16
|
+
streaming: boolean;
|
|
17
|
+
rawStreamLogs: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
maxBytesPerTurn: number;
|
|
20
|
+
retentionDays: number;
|
|
21
|
+
keepCompleted: boolean;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const DEEPCCC_HOME = join(homedir(), ".deepccc");
|
|
26
|
+
export const RAW_STREAM_LOGS_DIR = join(DEEPCCC_HOME, "raw-stream-logs");
|
|
27
|
+
const CONFIG_PATH = join(DEEPCCC_HOME, "config.json");
|
|
28
|
+
|
|
29
|
+
const DEFAULT_CONFIG: DeepCccConfig = {
|
|
30
|
+
provider: "openai",
|
|
31
|
+
apiKey: "",
|
|
32
|
+
baseURL: "https://api.deepseek.com/v1",
|
|
33
|
+
model: "deepseek-v4-pro",
|
|
34
|
+
effort: "",
|
|
35
|
+
streaming: true,
|
|
36
|
+
rawStreamLogs: {
|
|
37
|
+
enabled: false,
|
|
38
|
+
maxBytesPerTurn: 1024 * 1024,
|
|
39
|
+
retentionDays: 7,
|
|
40
|
+
keepCompleted: false,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function readConfigFile(): Partial<DeepCccConfig> {
|
|
45
|
+
if (!existsSync(CONFIG_PATH)) return {};
|
|
46
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Partial<DeepCccConfig>;
|
|
47
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function env(name: string): string | undefined {
|
|
51
|
+
const value = process.env[name]?.trim();
|
|
52
|
+
return value ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function boolEnv(name: string): boolean | undefined {
|
|
56
|
+
const value = env(name)?.toLowerCase();
|
|
57
|
+
if (value === undefined) return undefined;
|
|
58
|
+
if (["1", "true", "yes", "on"].includes(value)) return true;
|
|
59
|
+
if (["0", "false", "no", "off"].includes(value)) return false;
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function numberEnv(name: string): number | undefined {
|
|
64
|
+
const value = Number(env(name));
|
|
65
|
+
return Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function normalizeDeepCccProvider(value: unknown): DeepCccProvider {
|
|
69
|
+
if (value === undefined || value === null || String(value).trim() === "") return "openai";
|
|
70
|
+
const normalized = String(value).trim().toLowerCase();
|
|
71
|
+
if (normalized === "openai" || normalized === "anthropic") return normalized;
|
|
72
|
+
throw new Error(`DEEPCCC_PROVIDER/provider must be "openai" or "anthropic", received: ${String(value)}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function loadConfig(): DeepCccConfig {
|
|
76
|
+
const file = readConfigFile();
|
|
77
|
+
const rawLogs: Partial<DeepCccConfig["rawStreamLogs"]> = file.rawStreamLogs && typeof file.rawStreamLogs === "object"
|
|
78
|
+
? file.rawStreamLogs
|
|
79
|
+
: {};
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
provider: normalizeDeepCccProvider(env("DEEPCCC_PROVIDER") ?? file.provider ?? DEFAULT_CONFIG.provider),
|
|
83
|
+
apiKey: env("DEEPCCC_API_KEY") ?? env("DEEPSEEK_API_KEY") ?? file.apiKey ?? DEFAULT_CONFIG.apiKey,
|
|
84
|
+
baseURL: env("DEEPCCC_BASE_URL") ?? env("DEEPSEEK_BASE_URL") ?? file.baseURL ?? DEFAULT_CONFIG.baseURL,
|
|
85
|
+
model: env("DEEPCCC_MODEL") ?? env("DEEPSEEK_MODEL") ?? file.model ?? DEFAULT_CONFIG.model,
|
|
86
|
+
effort: env("DEEPCCC_EFFORT") ?? env("DEEPSEEK_EFFORT") ?? file.effort ?? DEFAULT_CONFIG.effort,
|
|
87
|
+
streaming: boolEnv("DEEPCCC_STREAMING") ?? file.streaming ?? DEFAULT_CONFIG.streaming,
|
|
88
|
+
rawStreamLogs: {
|
|
89
|
+
enabled: boolEnv("DEEPCCC_RAW_STREAM_LOGS") ?? rawLogs.enabled ?? DEFAULT_CONFIG.rawStreamLogs.enabled,
|
|
90
|
+
maxBytesPerTurn: numberEnv("DEEPCCC_RAW_STREAM_MAX_BYTES") ?? rawLogs.maxBytesPerTurn ?? DEFAULT_CONFIG.rawStreamLogs.maxBytesPerTurn,
|
|
91
|
+
retentionDays: numberEnv("DEEPCCC_RAW_STREAM_RETENTION_DAYS") ?? rawLogs.retentionDays ?? DEFAULT_CONFIG.rawStreamLogs.retentionDays,
|
|
92
|
+
keepCompleted: boolEnv("DEEPCCC_RAW_STREAM_KEEP_COMPLETED") ?? rawLogs.keepCompleted ?? DEFAULT_CONFIG.rawStreamLogs.keepCompleted,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function ensureConfigDir(): void {
|
|
98
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const config = loadConfig();
|
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
* ChatSession 是程序化入口,既可以被 CLI 调用,也可以被其他模块调用。
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
8
|
-
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
7
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
8
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
9
9
|
import { generateText, isLoopFinished, stepCountIs, streamText, type TextStreamPart } from "ai";
|
|
10
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
|
|
15
|
-
import {
|
|
16
|
-
config as appConfig,
|
|
17
|
-
normalizeDeepCccProvider,
|
|
18
|
-
RAW_STREAM_LOGS_DIR,
|
|
19
|
-
type DeepCccProvider,
|
|
20
|
-
} from "./config.js";
|
|
15
|
+
import {
|
|
16
|
+
config as appConfig,
|
|
17
|
+
normalizeDeepCccProvider,
|
|
18
|
+
RAW_STREAM_LOGS_DIR,
|
|
19
|
+
type DeepCccProvider,
|
|
20
|
+
} from "./config.js";
|
|
21
21
|
import {
|
|
22
22
|
createRawStreamLog,
|
|
23
23
|
type RawStreamLogHandle,
|
|
@@ -174,24 +174,24 @@ export function loadPlatformCommandPrompt(
|
|
|
174
174
|
return "";
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
function normalizeMaxSteps(value: number | undefined): number | undefined {
|
|
177
|
+
function normalizeMaxSteps(value: number | undefined): number | undefined {
|
|
178
178
|
if (value === undefined) return undefined;
|
|
179
179
|
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
180
180
|
throw new Error("maxSteps must be a positive integer when provided");
|
|
181
181
|
}
|
|
182
|
-
return value;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function normalizeAnthropicBaseURL(baseURL: string): string {
|
|
186
|
-
const normalized = baseURL.trim().replace(/\/+$/, "");
|
|
187
|
-
return normalized.endsWith("/v1") ? normalized : `${normalized}/v1`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export interface ChatSessionConfig {
|
|
191
|
-
/** API protocol/provider. Defaults to DEEPCCC_PROVIDER/config, then openai. */
|
|
192
|
-
provider?: DeepCccProvider;
|
|
193
|
-
/** Provider service base URL. Defaults to DEEPCCC_BASE_URL/config. */
|
|
194
|
-
baseURL?: string;
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function normalizeAnthropicBaseURL(baseURL: string): string {
|
|
186
|
+
const normalized = baseURL.trim().replace(/\/+$/, "");
|
|
187
|
+
return normalized.endsWith("/v1") ? normalized : `${normalized}/v1`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface ChatSessionConfig {
|
|
191
|
+
/** API protocol/provider. Defaults to DEEPCCC_PROVIDER/config, then openai. */
|
|
192
|
+
provider?: DeepCccProvider;
|
|
193
|
+
/** Provider service base URL. Defaults to DEEPCCC_BASE_URL/config. */
|
|
194
|
+
baseURL?: string;
|
|
195
195
|
/** API key. Defaults to DEEPCCC_API_KEY/config. */
|
|
196
196
|
apiKey?: string;
|
|
197
197
|
/** Model id. Defaults to DEEPCCC_MODEL/config. */
|
|
@@ -265,9 +265,9 @@ interface ChatMessage {
|
|
|
265
265
|
content: string;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
export class ChatSession {
|
|
269
|
-
private model: any;
|
|
270
|
-
private provider: DeepCccProvider;
|
|
268
|
+
export class ChatSession {
|
|
269
|
+
private model: any;
|
|
270
|
+
private provider: DeepCccProvider;
|
|
271
271
|
private cwd: string;
|
|
272
272
|
private context: BuiltinContextManager;
|
|
273
273
|
private compactionTimeoutMs: number;
|
|
@@ -290,26 +290,26 @@ export class ChatSession {
|
|
|
290
290
|
);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
const baseURL = overrides.baseURL ?? appConfig.baseURL;
|
|
294
|
-
const modelId = overrides.model ?? appConfig.model;
|
|
295
|
-
this.provider = normalizeDeepCccProvider(overrides.provider ?? appConfig.provider);
|
|
296
|
-
this.effort = (overrides.effort ?? appConfig.effort ?? "").trim();
|
|
297
|
-
|
|
298
|
-
if (this.provider === "anthropic") {
|
|
299
|
-
const provider = createAnthropic({
|
|
300
|
-
baseURL: normalizeAnthropicBaseURL(baseURL),
|
|
301
|
-
apiKey,
|
|
302
|
-
});
|
|
303
|
-
this.model = provider(modelId);
|
|
304
|
-
} else {
|
|
305
|
-
const provider = createOpenAICompatible({
|
|
306
|
-
name: "deepccc",
|
|
307
|
-
baseURL,
|
|
308
|
-
apiKey,
|
|
309
|
-
includeUsage: true,
|
|
310
|
-
});
|
|
311
|
-
this.model = provider(modelId);
|
|
312
|
-
}
|
|
293
|
+
const baseURL = overrides.baseURL ?? appConfig.baseURL;
|
|
294
|
+
const modelId = overrides.model ?? appConfig.model;
|
|
295
|
+
this.provider = normalizeDeepCccProvider(overrides.provider ?? appConfig.provider);
|
|
296
|
+
this.effort = (overrides.effort ?? appConfig.effort ?? "").trim();
|
|
297
|
+
|
|
298
|
+
if (this.provider === "anthropic") {
|
|
299
|
+
const provider = createAnthropic({
|
|
300
|
+
baseURL: normalizeAnthropicBaseURL(baseURL),
|
|
301
|
+
apiKey,
|
|
302
|
+
});
|
|
303
|
+
this.model = provider(modelId);
|
|
304
|
+
} else {
|
|
305
|
+
const provider = createOpenAICompatible({
|
|
306
|
+
name: "deepccc",
|
|
307
|
+
baseURL,
|
|
308
|
+
apiKey,
|
|
309
|
+
includeUsage: true,
|
|
310
|
+
});
|
|
311
|
+
this.model = provider(modelId);
|
|
312
|
+
}
|
|
313
313
|
this.cwd = options.cwd ?? process.cwd();
|
|
314
314
|
this.maxSteps = normalizeMaxSteps(options.maxSteps);
|
|
315
315
|
this.compactionTimeoutMs = Math.max(1, options.compactionTimeoutMs ?? DEFAULT_COMPACTION_TIMEOUT_MS);
|
|
@@ -407,29 +407,29 @@ export class ChatSession {
|
|
|
407
407
|
const skills = await scanSkillsDirs(this.skillDirs);
|
|
408
408
|
const system = this.buildSystemPrompt(skills);
|
|
409
409
|
this.systemPrompt = system;
|
|
410
|
-
const generationOptions = {
|
|
411
|
-
model: this.model,
|
|
412
|
-
system,
|
|
413
|
-
messages: this.context.buildModelMessages() as any,
|
|
410
|
+
const generationOptions = {
|
|
411
|
+
model: this.model,
|
|
412
|
+
system,
|
|
413
|
+
messages: this.context.buildModelMessages() as any,
|
|
414
414
|
tools: createBuiltinFileTools(this.cwd, { permissionGate: this.permissionGate }),
|
|
415
415
|
stopWhen: maxSteps !== undefined ? stepCountIs(maxSteps) : isLoopFinished(),
|
|
416
416
|
abortSignal: signal,
|
|
417
|
-
// DeepSeek OpenAI 兼容接口:providerOptions.deepseek.reasoningEffort
|
|
418
|
-
// 由 @ai-sdk/openai-compatible 自动映射为请求体 reasoning_effort 字段
|
|
419
|
-
...(this.provider === "openai" && this.effort
|
|
420
|
-
? { providerOptions: { deepseek: { reasoningEffort: this.effort } } }
|
|
421
|
-
: {}),
|
|
422
|
-
};
|
|
423
|
-
let stream: AsyncIterable<TextStreamPart<any>>;
|
|
424
|
-
if (appConfig.streaming) {
|
|
425
|
-
const result = streamText(generationOptions);
|
|
426
|
-
stream = result.fullStream ?? textStreamToFullStream(result.textStream);
|
|
427
|
-
} else {
|
|
428
|
-
const result = await generateText(generationOptions);
|
|
429
|
-
stream = generateResultToFullStream(result);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
for await (const part of stream as AsyncIterable<TextStreamPart<any>>) {
|
|
417
|
+
// DeepSeek OpenAI 兼容接口:providerOptions.deepseek.reasoningEffort
|
|
418
|
+
// 由 @ai-sdk/openai-compatible 自动映射为请求体 reasoning_effort 字段
|
|
419
|
+
...(this.provider === "openai" && this.effort
|
|
420
|
+
? { providerOptions: { deepseek: { reasoningEffort: this.effort } } }
|
|
421
|
+
: {}),
|
|
422
|
+
};
|
|
423
|
+
let stream: AsyncIterable<TextStreamPart<any>>;
|
|
424
|
+
if (appConfig.streaming) {
|
|
425
|
+
const result = streamText(generationOptions);
|
|
426
|
+
stream = result.fullStream ?? textStreamToFullStream(result.textStream);
|
|
427
|
+
} else {
|
|
428
|
+
const result = await generateText(generationOptions);
|
|
429
|
+
stream = generateResultToFullStream(result);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for await (const part of stream as AsyncIterable<TextStreamPart<any>>) {
|
|
433
433
|
rawLog?.writeLine(safeRawStreamJson(part));
|
|
434
434
|
if (part.type === "text-delta") {
|
|
435
435
|
fullText += part.text;
|
|
@@ -587,40 +587,40 @@ export class ChatSession {
|
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
-
async function* textStreamToFullStream(stream: AsyncIterable<string>): AsyncIterable<{ type: "text-delta"; text: string }> {
|
|
591
|
-
for await (const text of stream) {
|
|
592
|
-
yield { type: "text-delta", text };
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
async function* generateResultToFullStream(result: any): AsyncIterable<TextStreamPart<any>> {
|
|
597
|
-
let emittedText = false;
|
|
598
|
-
for (const step of result.steps ?? []) {
|
|
599
|
-
for (const call of step.toolCalls ?? []) {
|
|
600
|
-
yield {
|
|
601
|
-
type: "tool-call",
|
|
602
|
-
toolCallId: call.toolCallId,
|
|
603
|
-
toolName: call.toolName,
|
|
604
|
-
input: call.input,
|
|
605
|
-
} as TextStreamPart<any>;
|
|
606
|
-
}
|
|
607
|
-
for (const toolResult of step.toolResults ?? []) {
|
|
608
|
-
yield {
|
|
609
|
-
type: "tool-result",
|
|
610
|
-
toolCallId: toolResult.toolCallId,
|
|
611
|
-
toolName: toolResult.toolName,
|
|
612
|
-
output: toolResult.output,
|
|
613
|
-
} as TextStreamPart<any>;
|
|
614
|
-
}
|
|
615
|
-
if (step.text) {
|
|
616
|
-
emittedText = true;
|
|
617
|
-
yield { type: "text-delta", text: step.text } as TextStreamPart<any>;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
if (!emittedText && result.text) {
|
|
621
|
-
yield { type: "text-delta", text: result.text } as TextStreamPart<any>;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
590
|
+
async function* textStreamToFullStream(stream: AsyncIterable<string>): AsyncIterable<{ type: "text-delta"; text: string }> {
|
|
591
|
+
for await (const text of stream) {
|
|
592
|
+
yield { type: "text-delta", text };
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
async function* generateResultToFullStream(result: any): AsyncIterable<TextStreamPart<any>> {
|
|
597
|
+
let emittedText = false;
|
|
598
|
+
for (const step of result.steps ?? []) {
|
|
599
|
+
for (const call of step.toolCalls ?? []) {
|
|
600
|
+
yield {
|
|
601
|
+
type: "tool-call",
|
|
602
|
+
toolCallId: call.toolCallId,
|
|
603
|
+
toolName: call.toolName,
|
|
604
|
+
input: call.input,
|
|
605
|
+
} as TextStreamPart<any>;
|
|
606
|
+
}
|
|
607
|
+
for (const toolResult of step.toolResults ?? []) {
|
|
608
|
+
yield {
|
|
609
|
+
type: "tool-result",
|
|
610
|
+
toolCallId: toolResult.toolCallId,
|
|
611
|
+
toolName: toolResult.toolName,
|
|
612
|
+
output: toolResult.output,
|
|
613
|
+
} as TextStreamPart<any>;
|
|
614
|
+
}
|
|
615
|
+
if (step.text) {
|
|
616
|
+
emittedText = true;
|
|
617
|
+
yield { type: "text-delta", text: step.text } as TextStreamPart<any>;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (!emittedText && result.text) {
|
|
621
|
+
yield { type: "text-delta", text: result.text } as TextStreamPart<any>;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
624
|
|
|
625
625
|
function safeJson(value: unknown): string {
|
|
626
626
|
try {
|
package/package.json
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "chatccc",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "./src/index.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"chatccc": "bin/chatccc.mjs",
|
|
10
|
-
"cccagent": "bin/cccagent.mjs"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"src/",
|
|
14
|
-
"deepccc-agent/",
|
|
15
|
-
"bin/",
|
|
16
|
-
"scripts/postinstall-sharp-check.mjs",
|
|
17
|
-
"demo/ilink_echo_probe.ts",
|
|
18
|
-
"agent-prompts/",
|
|
19
|
-
"im-skills/",
|
|
20
|
-
".agents/skills/create-chatccc-feishu-app/",
|
|
21
|
-
".claude/skills/create-chatccc-feishu-app/",
|
|
22
|
-
".cursor/skills/create-chatccc-feishu-app/",
|
|
23
|
-
"images/img_readme_*.jpg",
|
|
24
|
-
"images/img_readme_*.png",
|
|
25
|
-
"images/avatars/status_*.png",
|
|
26
|
-
"images/avatars/badges/",
|
|
27
|
-
"images/avatars/combinations/",
|
|
28
|
-
"package.json",
|
|
29
|
-
"README.md",
|
|
30
|
-
"config.sample.json"
|
|
31
|
-
],
|
|
32
|
-
"scripts": {
|
|
33
|
-
"dev": "tsx src/index.ts",
|
|
34
|
-
"chatccc": "tsx src/index.ts",
|
|
35
|
-
"start": "tsx src/index.ts",
|
|
36
|
-
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
37
|
-
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
38
|
-
"demo:create-group": "tsx src/index.ts",
|
|
39
|
-
"demo:create-group:local": "tsx src/index.ts --local",
|
|
40
|
-
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
41
|
-
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
42
|
-
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
43
|
-
"demo:codex-app-server-approval": "tsx demo/codex-app-server-approval/approval_demo.ts",
|
|
44
|
-
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
45
|
-
"claude-proxy": "tsx src/litellm-proxy.ts",
|
|
46
|
-
"test": "vitest run",
|
|
47
|
-
"test:deepccc": "vitest run --root deepccc-agent",
|
|
48
|
-
"test:watch": "vitest",
|
|
49
|
-
"postinstall": "node scripts/postinstall-sharp-check.mjs"
|
|
50
|
-
},
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"@ai-sdk/anthropic": "^3.0.105",
|
|
53
|
-
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
54
|
-
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
55
|
-
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
56
|
-
"@vscode/ripgrep": "^1.18.0",
|
|
57
|
-
"ai": "^6.0.184",
|
|
58
|
-
"nodemailer": "^8.0.7",
|
|
59
|
-
"qrcode-terminal": "^0.12.0",
|
|
60
|
-
"sharp": "^0.34.5",
|
|
61
|
-
"tsx": "^4.0.0",
|
|
62
|
-
"ws": "^8.18.0"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"@types/node": "^20.0.0",
|
|
66
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
67
|
-
"@types/ws": "^8.18.1",
|
|
68
|
-
"typescript": "^5.0.0",
|
|
69
|
-
"vitest": "^3.2.4"
|
|
70
|
-
},
|
|
71
|
-
"engines": {
|
|
72
|
-
"node": ">=20"
|
|
73
|
-
}
|
|
74
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "chatccc",
|
|
3
|
+
"version": "0.2.247",
|
|
4
|
+
"description": "Feishu bot bridge for Claude Code",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"chatccc": "bin/chatccc.mjs",
|
|
10
|
+
"cccagent": "bin/cccagent.mjs"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src/",
|
|
14
|
+
"deepccc-agent/",
|
|
15
|
+
"bin/",
|
|
16
|
+
"scripts/postinstall-sharp-check.mjs",
|
|
17
|
+
"demo/ilink_echo_probe.ts",
|
|
18
|
+
"agent-prompts/",
|
|
19
|
+
"im-skills/",
|
|
20
|
+
".agents/skills/create-chatccc-feishu-app/",
|
|
21
|
+
".claude/skills/create-chatccc-feishu-app/",
|
|
22
|
+
".cursor/skills/create-chatccc-feishu-app/",
|
|
23
|
+
"images/img_readme_*.jpg",
|
|
24
|
+
"images/img_readme_*.png",
|
|
25
|
+
"images/avatars/status_*.png",
|
|
26
|
+
"images/avatars/badges/",
|
|
27
|
+
"images/avatars/combinations/",
|
|
28
|
+
"package.json",
|
|
29
|
+
"README.md",
|
|
30
|
+
"config.sample.json"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"chatccc": "tsx src/index.ts",
|
|
35
|
+
"start": "tsx src/index.ts",
|
|
36
|
+
"demo:bot-test": "tsx demo/bot_test.ts",
|
|
37
|
+
"demo:bot-test:local": "tsx demo/bot_test.ts --local",
|
|
38
|
+
"demo:create-group": "tsx src/index.ts",
|
|
39
|
+
"demo:create-group:local": "tsx src/index.ts --local",
|
|
40
|
+
"demo:permission-check": "tsx demo/permission_check.ts",
|
|
41
|
+
"demo:claude-hi": "tsx demo/claude_say_hi.ts",
|
|
42
|
+
"demo:codex-hi": "tsx demo/codex_say_hi.ts",
|
|
43
|
+
"demo:codex-app-server-approval": "tsx demo/codex-app-server-approval/approval_demo.ts",
|
|
44
|
+
"demo:ilink-echo": "tsx demo/ilink_echo_probe.ts",
|
|
45
|
+
"claude-proxy": "tsx src/litellm-proxy.ts",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"test:deepccc": "vitest run --root deepccc-agent",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"postinstall": "node scripts/postinstall-sharp-check.mjs"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@ai-sdk/anthropic": "^3.0.105",
|
|
53
|
+
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
54
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
55
|
+
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
56
|
+
"@vscode/ripgrep": "^1.18.0",
|
|
57
|
+
"ai": "^6.0.184",
|
|
58
|
+
"nodemailer": "^8.0.7",
|
|
59
|
+
"qrcode-terminal": "^0.12.0",
|
|
60
|
+
"sharp": "^0.34.5",
|
|
61
|
+
"tsx": "^4.0.0",
|
|
62
|
+
"ws": "^8.18.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^20.0.0",
|
|
66
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
67
|
+
"@types/ws": "^8.18.1",
|
|
68
|
+
"typescript": "^5.0.0",
|
|
69
|
+
"vitest": "^3.2.4"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20"
|
|
73
|
+
}
|
|
74
|
+
}
|