Merge branch 'main' of https://github.com/y574444354/csc
This commit is contained in:
commit
7806df4e3d
|
|
@ -0,0 +1,28 @@
|
||||||
|
# csc 项目 UI 文案可直接替换清单
|
||||||
|
|
||||||
|
- 目的:筛选出**只需要替换展示文案**、即可让 UI 页面显示 `CoStrict` 而不是 `Claude` / `Claude Code` / `Anthropic` 的内容。
|
||||||
|
- 筛选原则:
|
||||||
|
1. 仅保留用户可见文案,例如 `title`、`subtitle`、`message`、`placeholder`、`label`、`aria-label`、`<Text>` 文本。
|
||||||
|
2. 不纳入命令、包名、环境变量、URL、路径、主题 token、埋点名、注释说明。
|
||||||
|
3. 对账号体系、订阅体系、Desktop/Chrome 集成等文案,标记为“**可替换但需谨慎**”。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、高置信:可直接文本替换的 UI 文案
|
||||||
|
|
||||||
|
这部分基本都属于纯展示文本,直接替换后主要影响界面显示,不会直接触发命令、配置或协议变更。
|
||||||
|
|
||||||
|
| 文件 | 行号 | 原文 | 建议替换为 |
|
||||||
|
| --- | ---: | --- | --- |
|
||||||
|
| `packages/remote-control-server/web/index.html` | L6 | `Remote Control — Claude Code` | `Remote Control — CoStrict` |
|
||||||
|
| `packages/remote-control-server/web/automation.js` | L280 | `Claude Code is in proactive mode and currently sleeping until the next wake-up or user message.` | `CoStrict is in proactive mode and currently sleeping until the next wake-up or user message.` |
|
||||||
|
| `packages/remote-control-server/web/automation.js` | L290 | `Claude Code is in proactive mode and waiting for the next scheduled check-in.` | `CoStrict is in proactive mode and waiting for the next scheduled check-in.` |
|
||||||
|
| `packages/remote-control-server/web/automation.js` | L299 / L309 | `Claude Code is in proactive mode and may continue working between user messages.` | `CoStrict is in proactive mode and may continue working between user messages.` |
|
||||||
|
| `packages/remote-control-server/web/automation.js` | L319 | `Claude Code is processing an automatic background trigger.` | `CoStrict is processing an automatic background trigger.` |
|
||||||
|
| `packages/remote-control-server/web/automation.js` | L361 | `aria-label="Claude Code status"` | `aria-label="CoStrict status"` |
|
||||||
|
| `packages/@ant/ink/src/theme/LoadingState.tsx` | L45 | `Fetching your Claude Code sessions...` | `Fetching your CoStrict sessions...` |
|
||||||
|
| `src/components/FeedbackSurvey/FeedbackSurveyView.tsx` | L26 | `How is Claude doing this session? (optional)` | `How is CoStrict doing this session? (optional)` |
|
||||||
|
| `src/components/FeedbackSurvey/TranscriptSharePrompt.tsx` | L43 | `Can Anthropic look at your session transcript to help us improve` | `Can CoStrict look at your session transcript to help us improve` |
|
||||||
|
| `src/components/CostThresholdDialog.tsx` | L12 | `You've spent $5 on the Anthropic API this session.` | `You've spent $5 on the CoStrict API this session.` |
|
||||||
|
| `src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx` | L47 | `Description (tell Claude when to use this agent)` | `Description (tell CoStrict when to use this agent)` |
|
||||||
|
| `src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx` | L68 | `When should Claude use this agent?` | `When should CoStrict use this agent?` |
|
||||||
|
|
@ -42,7 +42,7 @@ type LoadingStateProps = {
|
||||||
* <LoadingState
|
* <LoadingState
|
||||||
* message="Loading sessions"
|
* message="Loading sessions"
|
||||||
* bold
|
* bold
|
||||||
* subtitle="Fetching your Claude Code sessions..."
|
* subtitle="Fetching your CoStrict sessions..."
|
||||||
* />
|
* />
|
||||||
*/
|
*/
|
||||||
export function LoadingState({
|
export function LoadingState({
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,7 @@ describe('DeepSeek thinking mode (enableThinking)', () => {
|
||||||
{ enableThinking: true },
|
{ enableThinking: true },
|
||||||
)
|
)
|
||||||
const assistant = result.filter(m => m.role === 'assistant')[0] as any
|
const assistant = result.filter(m => m.role === 'assistant')[0] as any
|
||||||
expect(assistant.reasoning_content).toBeUndefined()
|
expect(assistant.reasoning_content).toBe(' ')
|
||||||
})
|
})
|
||||||
|
|
||||||
// ── fix: reorder tool and user messages for OpenAI API compatibility (#168) ──
|
// ── fix: reorder tool and user messages for OpenAI API compatibility (#168) ──
|
||||||
|
|
|
||||||
|
|
@ -211,21 +211,29 @@ function convertInternalAssistantMessage(
|
||||||
const content = msg.message.content
|
const content = msg.message.content
|
||||||
|
|
||||||
if (typeof content === 'string') {
|
if (typeof content === 'string') {
|
||||||
return [
|
const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = {
|
||||||
{
|
role: 'assistant',
|
||||||
role: 'assistant',
|
content,
|
||||||
content,
|
}
|
||||||
} satisfies ChatCompletionAssistantMessageParam,
|
// 如果开启了thinking模式,必须提供reasoning_content字段
|
||||||
]
|
if (preserveReasoning) {
|
||||||
|
// content是字符串,没有reasoning内容,设置为空字符串
|
||||||
|
result.reasoning_content = ' '
|
||||||
|
}
|
||||||
|
return [result]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(content)) {
|
if (!Array.isArray(content)) {
|
||||||
return [
|
const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = {
|
||||||
{
|
role: 'assistant',
|
||||||
role: 'assistant',
|
content: '',
|
||||||
content: '',
|
}
|
||||||
} satisfies ChatCompletionAssistantMessageParam,
|
// 如果开启了thinking模式,必须提供reasoning_content字段
|
||||||
]
|
if (preserveReasoning) {
|
||||||
|
// content不是数组,没有reasoning内容,设置为空字符串
|
||||||
|
result.reasoning_content = ' '
|
||||||
|
}
|
||||||
|
return [result]
|
||||||
}
|
}
|
||||||
|
|
||||||
const textParts: string[] = []
|
const textParts: string[] = []
|
||||||
|
|
@ -258,11 +266,16 @@ function convertInternalAssistantMessage(
|
||||||
// Skip redacted_thinking, server_tool_use, etc.
|
// Skip redacted_thinking, server_tool_use, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: ChatCompletionAssistantMessageParam = {
|
const result: ChatCompletionAssistantMessageParam & { reasoning_content?: string } = {
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: textParts.length > 0 ? textParts.join('\n') : null,
|
content: textParts.length > 0 ? textParts.join('\n') : null,
|
||||||
...(toolCalls.length > 0 && { tool_calls: toolCalls }),
|
...(toolCalls.length > 0 && { tool_calls: toolCalls }),
|
||||||
...(reasoningParts.length > 0 && { reasoning_content: reasoningParts.join('\n') }),
|
}
|
||||||
|
|
||||||
|
// 如果开启了thinking模式,必须提供reasoning_content字段
|
||||||
|
if (preserveReasoning) {
|
||||||
|
// 如果有reasoning内容就用这些内容,没有才设置成空字符串
|
||||||
|
result.reasoning_content = reasoningParts.length > 0 ? reasoningParts.join('\n') : ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
return [result]
|
return [result]
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ export function getAutomationIndicator(state) {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "sleeping",
|
tone: "sleeping",
|
||||||
title: "Claude Code is in proactive mode and currently sleeping until the next wake-up or user message.",
|
title: "CoStrict is in proactive mode and currently sleeping until the next wake-up or user message.",
|
||||||
iconVariant: "sleeping",
|
iconVariant: "sleeping",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +287,7 @@ export function getAutomationIndicator(state) {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "proactive",
|
tone: "proactive",
|
||||||
title: "Claude Code is in proactive mode and waiting for the next scheduled check-in.",
|
title: "CoStrict is in proactive mode and waiting for the next scheduled check-in.",
|
||||||
iconVariant: "standby",
|
iconVariant: "standby",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +296,7 @@ export function getAutomationIndicator(state) {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "proactive",
|
tone: "proactive",
|
||||||
title: "Claude Code is in proactive mode and may continue working between user messages.",
|
title: "CoStrict is in proactive mode and may continue working between user messages.",
|
||||||
iconVariant: "active",
|
iconVariant: "active",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -306,7 +306,7 @@ export function getAutomationIndicator(state) {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "proactive",
|
tone: "proactive",
|
||||||
title: "Claude Code is in proactive mode and may continue working between user messages.",
|
title: "CoStrict is in proactive mode and may continue working between user messages.",
|
||||||
iconVariant: "active",
|
iconVariant: "active",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +316,7 @@ export function getAutomationIndicator(state) {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Auto Run",
|
label: "Auto Run",
|
||||||
tone: "auto-run",
|
tone: "auto-run",
|
||||||
title: "Claude Code is processing an automatic background trigger.",
|
title: "CoStrict is processing an automatic background trigger.",
|
||||||
iconVariant: "active",
|
iconVariant: "active",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -358,7 +358,7 @@ export function getAutomationActivity(state) {
|
||||||
|
|
||||||
export function renderAutomationIcon(variant = "active", { className = "", decorative = true } = {}) {
|
export function renderAutomationIcon(variant = "active", { className = "", decorative = true } = {}) {
|
||||||
const classes = ["clawd-icon", `clawd-icon-${variant}`, className].filter(Boolean).join(" ");
|
const classes = ["clawd-icon", `clawd-icon-${variant}`, className].filter(Boolean).join(" ");
|
||||||
const ariaAttrs = decorative ? 'aria-hidden="true"' : 'role="img" aria-label="Claude Code status"';
|
const ariaAttrs = decorative ? 'aria-hidden="true"' : 'role="img" aria-label="CoStrict status"';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<span class="${classes}" ${ariaAttrs}>
|
<span class="${classes}" ${ariaAttrs}>
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ describe("automation helpers", () => {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "proactive",
|
tone: "proactive",
|
||||||
title: "Claude Code is in proactive mode and may continue working between user messages.",
|
title: "CoStrict is in proactive mode and may continue working between user messages.",
|
||||||
iconVariant: "active",
|
iconVariant: "active",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ describe("automation helpers", () => {
|
||||||
visible: true,
|
visible: true,
|
||||||
label: "Autopilot",
|
label: "Autopilot",
|
||||||
tone: "proactive",
|
tone: "proactive",
|
||||||
title: "Claude Code is in proactive mode and waiting for the next scheduled check-in.",
|
title: "CoStrict is in proactive mode and waiting for the next scheduled check-in.",
|
||||||
iconVariant: "standby",
|
iconVariant: "standby",
|
||||||
});
|
});
|
||||||
expect(getAutomationActivity(state)).toEqual({
|
expect(getAutomationActivity(state)).toEqual({
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Remote Control — Claude Code</title>
|
<title>Remote Control — CoStrict</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,600;12..96,700&family=Figtree:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap" />
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,600;12..96,700&family=Figtree:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap" />
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import { generateMachineId, saveCoStrictCredentials } from '../../costrict/provi
|
||||||
import { extractExpiryFromJWT } from '../../costrict/provider/token.js';
|
import { extractExpiryFromJWT } from '../../costrict/provider/token.js';
|
||||||
import { updateSettingsForSource } from '../../utils/settings/settings.js';
|
import { updateSettingsForSource } from '../../utils/settings/settings.js';
|
||||||
import { useSetAppState as useSetGlobalAppState } from '../../state/AppState.js';
|
import { useSetAppState as useSetGlobalAppState } from '../../state/AppState.js';
|
||||||
|
import { isNotLoggedIn } from '../../utils/logoV2Utils.js';
|
||||||
|
|
||||||
function ModelPickerWrapper({
|
function ModelPickerWrapper({
|
||||||
onDone,
|
onDone,
|
||||||
|
|
@ -339,6 +340,15 @@ export const call: LocalJSXCommandCall = async (onDone, _context, args) => {
|
||||||
return <SetModelAndClose args={args} onDone={onDone} />;
|
return <SetModelAndClose args={args} onDone={onDone} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if user is logged in before showing model picker
|
||||||
|
if (isNotLoggedIn()) {
|
||||||
|
// User is not logged in, redirect to login
|
||||||
|
onDone('You need to login first. Use /login to authenticate.', {
|
||||||
|
display: 'system',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return <ModelPickerWrapper onDone={onDone} />;
|
return <ModelPickerWrapper onDone={onDone} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,8 @@ export function ConsoleOAuthFlow({
|
||||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||||
// Clear costrict env var to prevent conflicts
|
// Clear costrict env var to prevent conflicts
|
||||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||||
|
// Reset AppState model so logo reflects the new provider default
|
||||||
|
setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null }))
|
||||||
|
|
||||||
setOAuthStatus({ state: 'success' })
|
setOAuthStatus({ state: 'success' })
|
||||||
void sendNotification(
|
void sendNotification(
|
||||||
|
|
@ -734,6 +736,8 @@ function OAuthStatusMessage({
|
||||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||||
// Set default model to sonnet for this provider
|
// Set default model to sonnet for this provider
|
||||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||||
|
// Reset AppState model so logo reflects the new provider default
|
||||||
|
setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null }))
|
||||||
setOAuthStatus({ state: 'success' })
|
setOAuthStatus({ state: 'success' })
|
||||||
void onDone()
|
void onDone()
|
||||||
}
|
}
|
||||||
|
|
@ -958,6 +962,8 @@ function OAuthStatusMessage({
|
||||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||||
// Set default model to sonnet for this provider
|
// Set default model to sonnet for this provider
|
||||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||||
|
// Reset AppState model so logo reflects the new provider default
|
||||||
|
setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null }))
|
||||||
setOAuthStatus({ state: 'success' })
|
setOAuthStatus({ state: 'success' })
|
||||||
void onDone()
|
void onDone()
|
||||||
}
|
}
|
||||||
|
|
@ -1195,6 +1201,8 @@ function OAuthStatusMessage({
|
||||||
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
delete process.env.CLAUDE_CODE_USE_COSTRICT
|
||||||
// Set default model to sonnet for this provider
|
// Set default model to sonnet for this provider
|
||||||
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
updateSettingsForSource('userSettings', { model: getDefaultSonnetModel() } as any)
|
||||||
|
// Reset AppState model so logo reflects the new provider default
|
||||||
|
setAppState(prev => ({ ...prev, mainLoopModel: null, mainLoopModelForSession: null }))
|
||||||
setOAuthStatus({ state: 'success' })
|
setOAuthStatus({ state: 'success' })
|
||||||
void onDone()
|
void onDone()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ type Props = {
|
||||||
export function CostThresholdDialog({ onDone }: Props): React.ReactNode {
|
export function CostThresholdDialog({ onDone }: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
title="You've spent $5 on the Anthropic API this session."
|
title="You've spent $5 on the CoStrict API this session."
|
||||||
onCancel={onDone}
|
onCancel={onDone}
|
||||||
>
|
>
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const inputToResponse: Record<ResponseInput, FeedbackSurveyResponse> = {
|
||||||
export const isValidResponseInput = (input: string): input is ResponseInput =>
|
export const isValidResponseInput = (input: string): input is ResponseInput =>
|
||||||
(RESPONSE_INPUTS as readonly string[]).includes(input)
|
(RESPONSE_INPUTS as readonly string[]).includes(input)
|
||||||
|
|
||||||
const DEFAULT_MESSAGE = 'How is Claude doing this session? (optional)'
|
const DEFAULT_MESSAGE = 'How is CoStrict doing this session? (optional)'
|
||||||
|
|
||||||
export function FeedbackSurveyView({
|
export function FeedbackSurveyView({
|
||||||
onSelect,
|
onSelect,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ export function TranscriptSharePrompt({
|
||||||
<Box>
|
<Box>
|
||||||
<Text color="ansi:cyan">{BLACK_CIRCLE} </Text>
|
<Text color="ansi:cyan">{BLACK_CIRCLE} </Text>
|
||||||
<Text bold>
|
<Text bold>
|
||||||
Can Anthropic look at your session transcript to help us improve
|
Can the CoStrict team look at your session transcript to help us
|
||||||
CoStrict?
|
improve CoStrict?
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export function DescriptionStep(): ReactNode {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WizardDialogLayout
|
<WizardDialogLayout
|
||||||
subtitle="Description (tell Claude when to use this agent)"
|
subtitle="Description (tell CoStrict when to use this agent)"
|
||||||
footerText={
|
footerText={
|
||||||
<Byline>
|
<Byline>
|
||||||
<KeyboardShortcutHint shortcut="Type" action="enter text" />
|
<KeyboardShortcutHint shortcut="Type" action="enter text" />
|
||||||
|
|
@ -65,7 +65,7 @@ export function DescriptionStep(): ReactNode {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
<Text>When should Claude use this agent?</Text>
|
<Text>When should CoStrict use this agent?</Text>
|
||||||
|
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export function UltraplanChoiceDialog({
|
||||||
if (transcriptSaved) {
|
if (transcriptSaved) {
|
||||||
setMessages(prev => [
|
setMessages(prev => [
|
||||||
...prev,
|
...prev,
|
||||||
createSystemMessage(`Previous session saved · resume with: claude --resume ${previousSessionId}`, 'suggestion'),
|
createSystemMessage(`Previous session saved · resume with: csc --resume ${previousSessionId}`, 'suggestion'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { createCoStrictFetch } from './fetch.js'
|
||||||
import { resolveCoStrictModel } from './modelMapping.js'
|
import { resolveCoStrictModel } from './modelMapping.js'
|
||||||
import { getCoStrictBaseURL } from './auth.js'
|
import { getCoStrictBaseURL } from './auth.js'
|
||||||
import { loadCoStrictCredentials } from './credentials.js'
|
import { loadCoStrictCredentials } from './credentials.js'
|
||||||
|
import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CoStrict 查询路径
|
* CoStrict 查询路径
|
||||||
|
|
@ -80,9 +81,12 @@ export async function* queryModelCoStrict(
|
||||||
)
|
)
|
||||||
|
|
||||||
// 5. 转换为 OpenAI 格式
|
// 5. 转换为 OpenAI 格式
|
||||||
|
// 根据模型名称自动检测是否启用thinking模式
|
||||||
|
const enableThinking = isOpenAIThinkingEnabled(costrictModel)
|
||||||
const openaiMessages = anthropicMessagesToOpenAI(
|
const openaiMessages = anthropicMessagesToOpenAI(
|
||||||
messagesForAPI,
|
messagesForAPI,
|
||||||
systemPrompt
|
systemPrompt,
|
||||||
|
{ enableThinking }
|
||||||
)
|
)
|
||||||
const openaiTools = anthropicToolsToOpenAI(standardTools)
|
const openaiTools = anthropicToolsToOpenAI(standardTools)
|
||||||
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
|
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ function getModelFamily(model: string): 'haiku' | 'sonnet' | 'opus' | null {
|
||||||
*
|
*
|
||||||
* 优先级:
|
* 优先级:
|
||||||
* 1. 传入的 model 本身就是已知的 CoStrict 模型 ID(用户通过 /model 明确选择)
|
* 1. 传入的 model 本身就是已知的 CoStrict 模型 ID(用户通过 /model 明确选择)
|
||||||
* 2. COSTRICT_MODEL 环境变量(管理员全局覆盖 / 登录默认值)
|
* 2. COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量(按模型族)
|
||||||
* 3. COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量(按模型族)
|
* 3. 直接透传原始模型名(用户手动配置的模型名)
|
||||||
* 4. 已缓存的 CoStrict 模型列表中的第一个
|
* 4. COSTRICT_MODEL 环境变量(仅作为最终兜底)
|
||||||
* 5. 直接透传原始模型名(最后兜底)
|
|
||||||
*/
|
*/
|
||||||
export function resolveCoStrictModel(anthropicModel: string): string {
|
export function resolveCoStrictModel(anthropicModel: string): string {
|
||||||
const cleanModel = anthropicModel.replace(/\[1m\]$/, '')
|
const cleanModel = anthropicModel.replace(/\[1m\]$/, '')
|
||||||
|
|
@ -29,10 +28,7 @@ export function resolveCoStrictModel(anthropicModel: string): string {
|
||||||
const cached = getCachedCoStrictModels()
|
const cached = getCachedCoStrictModels()
|
||||||
if (cached.some(m => m.id === cleanModel)) return cleanModel
|
if (cached.some(m => m.id === cleanModel)) return cleanModel
|
||||||
|
|
||||||
// 优先级 2: COSTRICT_MODEL 环境变量
|
// 优先级 2: COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量
|
||||||
if (process.env.COSTRICT_MODEL) return process.env.COSTRICT_MODEL
|
|
||||||
|
|
||||||
// 优先级 3: COSTRICT_DEFAULT_{FAMILY}_MODEL 环境变量
|
|
||||||
const family = getModelFamily(cleanModel)
|
const family = getModelFamily(cleanModel)
|
||||||
if (family) {
|
if (family) {
|
||||||
const envVar = `COSTRICT_DEFAULT_${family.toUpperCase()}_MODEL`
|
const envVar = `COSTRICT_DEFAULT_${family.toUpperCase()}_MODEL`
|
||||||
|
|
@ -40,9 +36,6 @@ export function resolveCoStrictModel(anthropicModel: string): string {
|
||||||
if (override) return override
|
if (override) return override
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先级 4: 缓存中的第一个模型
|
// 优先级 3: 直接透传原始模型名(用户手动配置的模型名优先于环境变量)
|
||||||
if (cached.length > 0) return cached[0].id
|
|
||||||
|
|
||||||
// 优先级 5: 透传原始模型名
|
|
||||||
return cleanModel
|
return cleanModel
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -721,7 +721,7 @@ function TranscriptSearchBar({
|
||||||
}
|
}
|
||||||
|
|
||||||
const TITLE_ANIMATION_FRAMES = ['⠂', '⠐'];
|
const TITLE_ANIMATION_FRAMES = ['⠂', '⠐'];
|
||||||
const TITLE_STATIC_PREFIX = '✳';
|
const TITLE_STATIC_PREFIX = '';
|
||||||
const TITLE_ANIMATION_INTERVAL_MS = 960;
|
const TITLE_ANIMATION_INTERVAL_MS = 960;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ export function isOpenAIThinkingEnabled(model: string): boolean {
|
||||||
if (isEnvDefinedFalsy(process.env.OPENAI_ENABLE_THINKING)) return false
|
if (isEnvDefinedFalsy(process.env.OPENAI_ENABLE_THINKING)) return false
|
||||||
// Explicit enable
|
// Explicit enable
|
||||||
if (isEnvTruthy(process.env.OPENAI_ENABLE_THINKING)) return true
|
if (isEnvTruthy(process.env.OPENAI_ENABLE_THINKING)) return true
|
||||||
// Auto-detect from model name (deepseek-reasoner and DeepSeek-V3.2 support thinking mode)
|
// Auto-detect from model name (deepseek-reasoner, DeepSeek-V3.2, kimi support thinking mode)
|
||||||
const modelLower = model.toLowerCase()
|
const modelLower = model.toLowerCase()
|
||||||
return modelLower.includes('deepseek-reasoner') || modelLower.includes('deepseek-v3.2')
|
return modelLower.includes('deepseek-reasoner') || modelLower.includes('deepseek-v3.2') || modelLower.includes('kimi')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ const externalTips: Tip[] = [
|
||||||
{
|
{
|
||||||
id: 'continue',
|
id: 'continue',
|
||||||
content: async () =>
|
content: async () =>
|
||||||
'Run claude --continue or claude --resume to resume a conversation',
|
'Run csc --continue or csc --resume to resume a conversation',
|
||||||
cooldownSessions: 10,
|
cooldownSessions: 10,
|
||||||
isRelevant: async () => true,
|
isRelevant: async () => true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -433,9 +433,8 @@ export async function setup(
|
||||||
// 预取模型列表,填充同步缓存
|
// 预取模型列表,填充同步缓存
|
||||||
const baseUrl = getCoStrictBaseURL(activeCreds.base_url)
|
const baseUrl = getCoStrictBaseURL(activeCreds.base_url)
|
||||||
const models = await fetchCoStrictModels(baseUrl, activeCreds.access_token)
|
const models = await fetchCoStrictModels(baseUrl, activeCreds.access_token)
|
||||||
if (models.length > 0 && !process.env.COSTRICT_MODEL) {
|
// 模型列表已预取并缓存,不再自动设置 COSTRICT_MODEL 环境变量
|
||||||
process.env.COSTRICT_MODEL = models[0].id
|
// resolveCoStrictModel() 会直接透传用户配置的模型名
|
||||||
}
|
|
||||||
process.env.CLAUDE_CODE_USE_COSTRICT = '1'
|
process.env.CLAUDE_CODE_USE_COSTRICT = '1'
|
||||||
} catch {
|
} catch {
|
||||||
// 初始化失败不阻断启动
|
// 初始化失败不阻断启动
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export function checkCrossProjectResume(
|
||||||
// Gate worktree detection to ants only for staged rollout
|
// Gate worktree detection to ants only for staged rollout
|
||||||
if (process.env.USER_TYPE !== 'ant') {
|
if (process.env.USER_TYPE !== 'ant') {
|
||||||
const sessionId = getSessionIdFromLog(log)
|
const sessionId = getSessionIdFromLog(log)
|
||||||
const command = `cd ${quote([log.projectPath])} && claude --resume ${sessionId}`
|
const command = `cd ${quote([log.projectPath])} && csc --resume ${sessionId}`
|
||||||
return {
|
return {
|
||||||
isCrossProject: true,
|
isCrossProject: true,
|
||||||
isSameRepoWorktree: false,
|
isSameRepoWorktree: false,
|
||||||
|
|
@ -65,7 +65,7 @@ export function checkCrossProjectResume(
|
||||||
|
|
||||||
// Different repo - generate cd command
|
// Different repo - generate cd command
|
||||||
const sessionId = getSessionIdFromLog(log)
|
const sessionId = getSessionIdFromLog(log)
|
||||||
const command = `cd ${quote([log.projectPath])} && claude --resume ${sessionId}`
|
const command = `cd ${quote([log.projectPath])} && csc --resume ${sessionId}`
|
||||||
return {
|
return {
|
||||||
isCrossProject: true,
|
isCrossProject: true,
|
||||||
isSameRepoWorktree: false,
|
isSameRepoWorktree: false,
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ function printResumeHint(): void {
|
||||||
writeSync(
|
writeSync(
|
||||||
1,
|
1,
|
||||||
chalk.dim(
|
chalk.dim(
|
||||||
`\nResume this session with:\nclaude --resume ${resumeArg}\n`,
|
`\nResume this session with:\ncsc --resume ${resumeArg}\n`,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
resumeHintPrinted = true
|
resumeHintPrinted = true
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ export type ModelSetting = ModelName | ModelAlias | null
|
||||||
|
|
||||||
export function getSmallFastModel(): ModelName {
|
export function getSmallFastModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
|
// CoStrict has no haiku alias — use the main loop model to stay on the same provider
|
||||||
|
if (provider === 'costrict') return getMainLoopModel()
|
||||||
// Provider-specific small fast model
|
// Provider-specific small fast model
|
||||||
if (provider === 'openai' && process.env.OPENAI_SMALL_FAST_MODEL) {
|
if (provider === 'openai' && process.env.OPENAI_SMALL_FAST_MODEL) {
|
||||||
return process.env.OPENAI_SMALL_FAST_MODEL
|
return process.env.OPENAI_SMALL_FAST_MODEL
|
||||||
|
|
@ -149,6 +151,8 @@ export function getDefaultOpusModel(): ModelName {
|
||||||
// @[MODEL LAUNCH]: Update the default Sonnet model (3P providers may lag so keep defaults unchanged).
|
// @[MODEL LAUNCH]: Update the default Sonnet model (3P providers may lag so keep defaults unchanged).
|
||||||
export function getDefaultSonnetModel(): ModelName {
|
export function getDefaultSonnetModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
|
// CoStrict has no sonnet alias — use the main loop model to stay on the same provider
|
||||||
|
if (provider === 'costrict') return getMainLoopModel()
|
||||||
// For OpenAI provider, check OPENAI_DEFAULT_SONNET_MODEL first
|
// For OpenAI provider, check OPENAI_DEFAULT_SONNET_MODEL first
|
||||||
if (provider === 'openai' && process.env.OPENAI_DEFAULT_SONNET_MODEL) {
|
if (provider === 'openai' && process.env.OPENAI_DEFAULT_SONNET_MODEL) {
|
||||||
return process.env.OPENAI_DEFAULT_SONNET_MODEL
|
return process.env.OPENAI_DEFAULT_SONNET_MODEL
|
||||||
|
|
@ -171,6 +175,8 @@ export function getDefaultSonnetModel(): ModelName {
|
||||||
// @[MODEL LAUNCH]: Update the default Haiku model (3P providers may lag so keep defaults unchanged).
|
// @[MODEL LAUNCH]: Update the default Haiku model (3P providers may lag so keep defaults unchanged).
|
||||||
export function getDefaultHaikuModel(): ModelName {
|
export function getDefaultHaikuModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
|
// CoStrict has no haiku alias — use the main loop model to stay on the same provider
|
||||||
|
if (provider === 'costrict') return getMainLoopModel()
|
||||||
// For OpenAI provider, check OPENAI_DEFAULT_HAIKU_MODEL first
|
// For OpenAI provider, check OPENAI_DEFAULT_HAIKU_MODEL first
|
||||||
if (provider === 'openai' && process.env.OPENAI_DEFAULT_HAIKU_MODEL) {
|
if (provider === 'openai' && process.env.OPENAI_DEFAULT_HAIKU_MODEL) {
|
||||||
return process.env.OPENAI_DEFAULT_HAIKU_MODEL
|
return process.env.OPENAI_DEFAULT_HAIKU_MODEL
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
|
// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered
|
||||||
import { getInitialMainLoopModel } from '../../bootstrap/state.js'
|
|
||||||
import {
|
import {
|
||||||
isClaudeAISubscriber,
|
isClaudeAISubscriber,
|
||||||
isMaxSubscriber,
|
isMaxSubscriber,
|
||||||
|
|
@ -25,7 +24,6 @@ import {
|
||||||
getDefaultHaikuModel,
|
getDefaultHaikuModel,
|
||||||
getDefaultMainLoopModelSetting,
|
getDefaultMainLoopModelSetting,
|
||||||
getMarketingNameForModel,
|
getMarketingNameForModel,
|
||||||
getUserSpecifiedModelSetting,
|
|
||||||
isOpus1mMergeEnabled,
|
isOpus1mMergeEnabled,
|
||||||
getOpus46PricingSuffix,
|
getOpus46PricingSuffix,
|
||||||
renderDefaultModelSetting,
|
renderDefaultModelSetting,
|
||||||
|
|
@ -542,45 +540,31 @@ export function getModelOptions(fastMode = false): ModelOption[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add custom model from either the current model value or the initial one
|
// Add ANTHROPIC_MODEL env var model as a custom option, but only for
|
||||||
// if it is not already in the options.
|
// anthropic-compatible providers. settings.model is NOT added here.
|
||||||
let customModel: ModelSetting = null
|
const provider = getAPIProvider()
|
||||||
const currentMainLoopModel = getUserSpecifiedModelSetting()
|
const envModel =
|
||||||
const initialMainLoopModel = getInitialMainLoopModel()
|
provider === 'firstParty' ||
|
||||||
if (currentMainLoopModel !== undefined && currentMainLoopModel !== null) {
|
provider === 'bedrock' ||
|
||||||
customModel = currentMainLoopModel
|
provider === 'vertex' ||
|
||||||
} else if (initialMainLoopModel !== null) {
|
provider === 'foundry'
|
||||||
customModel = initialMainLoopModel
|
? process.env.ANTHROPIC_MODEL
|
||||||
}
|
: undefined
|
||||||
if (customModel === null || options.some(opt => opt.value === customModel)) {
|
|
||||||
return filterModelOptionsByAllowlist(options)
|
if (envModel && !options.some(opt => opt.value === envModel)) {
|
||||||
} else if (customModel === 'opusplan') {
|
const knownOption = getKnownModelOption(envModel)
|
||||||
return filterModelOptionsByAllowlist([...options, getOpusPlanOption()])
|
|
||||||
} else if (customModel === 'opus' && getAPIProvider() === 'firstParty') {
|
|
||||||
return filterModelOptionsByAllowlist([
|
|
||||||
...options,
|
|
||||||
getMaxOpusOption(fastMode),
|
|
||||||
])
|
|
||||||
} else if (customModel === 'opus[1m]' && getAPIProvider() === 'firstParty') {
|
|
||||||
return filterModelOptionsByAllowlist([
|
|
||||||
...options,
|
|
||||||
getMergedOpus1MOption(fastMode),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
// Try to show a human-readable label for known Anthropic models, with an
|
|
||||||
// upgrade hint if the alias now resolves to a newer version.
|
|
||||||
const knownOption = getKnownModelOption(customModel)
|
|
||||||
if (knownOption) {
|
if (knownOption) {
|
||||||
options.push(knownOption)
|
options.push(knownOption)
|
||||||
} else {
|
} else {
|
||||||
options.push({
|
options.push({
|
||||||
value: customModel,
|
value: envModel,
|
||||||
label: customModel,
|
label: envModel,
|
||||||
description: 'Custom model',
|
description: 'Custom model (ANTHROPIC_MODEL)',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return filterModelOptionsByAllowlist(options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return filterModelOptionsByAllowlist(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ const MAX_RELEASE_NOTES_SHOWN = 5
|
||||||
* 3. Next time the user starts Claude, the cached changelog is available immediately
|
* 3. Next time the user starts Claude, the cached changelog is available immediately
|
||||||
*/
|
*/
|
||||||
export const CHANGELOG_URL =
|
export const CHANGELOG_URL =
|
||||||
'https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md'
|
'https://github.com/y574444354/csc/blob/main/CHANGELOG.md'
|
||||||
const RAW_CHANGELOG_URL =
|
const RAW_CHANGELOG_URL =
|
||||||
'https://raw.githubusercontent.com/anthropics/claude-code/refs/heads/main/CHANGELOG.md'
|
'https://raw.githubusercontent.com/y574444354/csc/refs/heads/main/CHANGELOG.md'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the path for the cached changelog file.
|
* Get the path for the cached changelog file.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user