mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-09-24 07:25:12 -05:00
Improve warning notice formatting (#1415)
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
|
||||
import type { StatusNoticeContext } from './statusNoticeDefinitions.js'
|
||||
import { getActiveNotices } from './statusNoticeDefinitions.js'
|
||||
import {
|
||||
getActiveNotices,
|
||||
statusNoticeDefinitions,
|
||||
} from './statusNoticeDefinitions.js'
|
||||
import { renderToString } from './staticRender.js'
|
||||
|
||||
// Regression coverage for issue #244 — the two safety-related status notices
|
||||
// that warn 3P users when they are running without the AI classifier or with
|
||||
@@ -21,6 +25,15 @@ function activeIds(ctx: StatusNoticeContext): string[] {
|
||||
return getActiveNotices(ctx).map(n => n.id)
|
||||
}
|
||||
|
||||
async function renderNoticePlainText(
|
||||
id: string,
|
||||
ctx: StatusNoticeContext,
|
||||
): Promise<string> {
|
||||
const notice = statusNoticeDefinitions.find(n => n.id === id)
|
||||
expect(notice).toBeDefined()
|
||||
return renderToString(notice!.render(ctx), 80)
|
||||
}
|
||||
|
||||
const SAVED_ARGV = process.argv
|
||||
const SAVED_API_KEY = process.env.ANTHROPIC_API_KEY
|
||||
|
||||
@@ -139,3 +152,42 @@ describe('dangerously-skip-permissions sandbox notice (#244 finding 2)', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('safety notice rendering', () => {
|
||||
test('separates warning icons from the notice text', async () => {
|
||||
const ctx = buildContext({
|
||||
permissionMode: 'bypassPermissions',
|
||||
mainLoopModel: 'llama3.1',
|
||||
})
|
||||
|
||||
const thirdPartyNotice = await renderNoticePlainText(
|
||||
'third-party-permissive-mode',
|
||||
ctx,
|
||||
)
|
||||
const dangerouslySkipNotice = await renderNoticePlainText(
|
||||
'dangerously-skip-permissions-no-sandbox',
|
||||
ctx,
|
||||
)
|
||||
|
||||
expect(thirdPartyNotice).toContain('⚠ bypassPermissions')
|
||||
expect(thirdPartyNotice).not.toContain('⚠bypassPermissions')
|
||||
expect(dangerouslySkipNotice).toContain(
|
||||
'⚠ --dangerously-skip-permissions',
|
||||
)
|
||||
expect(dangerouslySkipNotice).not.toContain(
|
||||
'⚠--dangerously-skip-permissions',
|
||||
)
|
||||
expect(
|
||||
thirdPartyNotice
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
.every(line => line.startsWith(' ')),
|
||||
).toBe(true)
|
||||
expect(
|
||||
dangerouslySkipNotice
|
||||
.split('\n')
|
||||
.slice(1)
|
||||
.every(line => line.startsWith(' ')),
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,6 +35,23 @@ export type StatusNoticeDefinition = {
|
||||
render: (context: StatusNoticeContext) => React.ReactNode;
|
||||
};
|
||||
|
||||
function WarningNoticeRow({
|
||||
children,
|
||||
marginTop,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
marginTop?: number;
|
||||
}): React.ReactNode {
|
||||
return <Box flexDirection="row" marginTop={marginTop}>
|
||||
<Box marginRight={1}>
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
</Box>
|
||||
<Box flexDirection="column" flexShrink={1}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
// Individual notice definitions
|
||||
const largeMemoryFilesNotice: StatusNoticeDefinition = {
|
||||
id: 'large-memory-files',
|
||||
@@ -45,15 +62,14 @@ const largeMemoryFilesNotice: StatusNoticeDefinition = {
|
||||
return <>
|
||||
{largeMemoryFiles.map(file => {
|
||||
const displayPath = file.path.startsWith(getCwd()) ? relative(getCwd(), file.path) : file.path;
|
||||
return <Box key={file.path} flexDirection="row">
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
return <WarningNoticeRow key={file.path}>
|
||||
<Text color="warning">
|
||||
Large <Text bold>{displayPath}</Text> will impact performance (
|
||||
{formatNumber(file.content.length)} chars >{' '}
|
||||
{formatNumber(MAX_MEMORY_CHARACTER_COUNT)})
|
||||
<Text dimColor> · /memory to edit</Text>
|
||||
</Text>
|
||||
</Box>;
|
||||
</WarningNoticeRow>;
|
||||
})}
|
||||
</>;
|
||||
}
|
||||
@@ -67,14 +83,13 @@ const claudeAiSubscriberExternalTokenNotice: StatusNoticeDefinition = {
|
||||
},
|
||||
render: () => {
|
||||
const authTokenInfo = getAuthTokenSource();
|
||||
return <Box flexDirection="row" marginTop={1}>
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
return <WarningNoticeRow marginTop={1}>
|
||||
<Text color="warning">
|
||||
Auth conflict: Using {authTokenInfo.source} instead of Claude account
|
||||
subscription token. Either unset {authTokenInfo.source}, or run
|
||||
`claude /logout`.
|
||||
</Text>
|
||||
</Box>;
|
||||
</WarningNoticeRow>;
|
||||
}
|
||||
};
|
||||
const apiKeyConflictNotice: StatusNoticeDefinition = {
|
||||
@@ -94,13 +109,12 @@ const apiKeyConflictNotice: StatusNoticeDefinition = {
|
||||
} = getAnthropicApiKeyWithSource({
|
||||
skipRetrievingKeyFromApiKeyHelper: true
|
||||
});
|
||||
return <Box flexDirection="row" marginTop={1}>
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
return <WarningNoticeRow marginTop={1}>
|
||||
<Text color="warning">
|
||||
Auth conflict: Using {apiKeySource} instead of Anthropic Console key.
|
||||
Either unset {apiKeySource}, or run `openclaude /logout`.
|
||||
</Text>
|
||||
</Box>;
|
||||
</WarningNoticeRow>;
|
||||
}
|
||||
};
|
||||
const bothAuthMethodsNotice: StatusNoticeDefinition = {
|
||||
@@ -123,13 +137,12 @@ const bothAuthMethodsNotice: StatusNoticeDefinition = {
|
||||
});
|
||||
const authTokenInfo = getAuthTokenSource();
|
||||
return <Box flexDirection="column" marginTop={1}>
|
||||
<Box flexDirection="row">
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
<WarningNoticeRow>
|
||||
<Text color="warning">
|
||||
Auth conflict: Both a token ({authTokenInfo.source}) and an API key
|
||||
({apiKeySource}) are set. This may lead to unexpected behavior.
|
||||
</Text>
|
||||
</Box>
|
||||
</WarningNoticeRow>
|
||||
<Box flexDirection="column" marginLeft={3}>
|
||||
<Text color="warning">
|
||||
· Trying to use{' '}
|
||||
@@ -154,15 +167,14 @@ const largeAgentDescriptionsNotice: StatusNoticeDefinition = {
|
||||
},
|
||||
render: context => {
|
||||
const totalTokens = getAgentDescriptionsTotalTokens(context.agentDefinitions);
|
||||
return <Box flexDirection="row">
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
return <WarningNoticeRow>
|
||||
<Text color="warning">
|
||||
Large cumulative agent descriptions will impact performance (~
|
||||
{formatNumber(totalTokens)} tokens >{' '}
|
||||
{formatNumber(AGENT_DESCRIPTIONS_THRESHOLD)})
|
||||
<Text dimColor> · /agents to manage</Text>
|
||||
</Text>
|
||||
</Box>;
|
||||
</WarningNoticeRow>;
|
||||
}
|
||||
};
|
||||
const jetbrainsPluginNotice: StatusNoticeDefinition = {
|
||||
@@ -224,14 +236,15 @@ const thirdPartyPermissiveModeNotice: StatusNoticeDefinition = {
|
||||
},
|
||||
render: ctx => {
|
||||
const mode = ctx.permissionMode;
|
||||
return <Box flexDirection="row">
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
return <WarningNoticeRow>
|
||||
<Text color="warning">
|
||||
<Text bold>{mode}</Text> mode is active on a third-party provider —
|
||||
tool calls run without the AI safety classifier.
|
||||
<Text dimColor> Inspect tool calls manually, especially when working with untrusted code.</Text>
|
||||
<Text bold>{mode}</Text> mode is active on a third-party provider.
|
||||
</Text>
|
||||
</Box>;
|
||||
<Text dimColor>
|
||||
Tool calls run without the AI safety classifier. Inspect tool calls manually,
|
||||
especially when working with untrusted code.
|
||||
</Text>
|
||||
</WarningNoticeRow>;
|
||||
}
|
||||
};
|
||||
// `--dangerously-skip-permissions` (a.k.a. bypassPermissions) auto-approves
|
||||
@@ -250,14 +263,15 @@ const dangerouslySkipPermissionsNotice: StatusNoticeDefinition = {
|
||||
isActive: ctx =>
|
||||
hasDangerouslySkipPermissionsArg() ||
|
||||
ctx.permissionMode === 'bypassPermissions',
|
||||
render: () => <Box flexDirection="row">
|
||||
<Text color="warning">{figures.warning}</Text>
|
||||
render: () => <WarningNoticeRow>
|
||||
<Text color="warning">
|
||||
<Text bold>--dangerously-skip-permissions</Text> bypasses every tool
|
||||
consent check.
|
||||
<Text dimColor> Only use inside a sandbox with no internet access. Restart without the flag to re-enable prompts.</Text>
|
||||
<Text bold>--dangerously-skip-permissions</Text> is active.
|
||||
</Text>
|
||||
</Box>
|
||||
<Text dimColor>
|
||||
Every tool consent check is bypassed. Only use inside a sandbox with no internet access.
|
||||
Restart without the flag to re-enable prompts.
|
||||
</Text>
|
||||
</WarningNoticeRow>
|
||||
};
|
||||
|
||||
// All notice definitions
|
||||
|
||||
Reference in New Issue
Block a user