refactor(cli): centralize system information collection

This commit is contained in:
pomelo-nwu
2025-11-06 10:42:52 +08:00
parent 553a36302a
commit 82170e96c6
12 changed files with 975 additions and 378 deletions

View File

@@ -8,38 +8,22 @@ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { aboutCommand } from './aboutCommand.js';
import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import * as versionUtils from '../../utils/version.js';
import { MessageType } from '../types.js';
import { IdeClient } from '@qwen-code/qwen-code-core';
import * as systemInfoUtils from '../../utils/systemInfo.js';
vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {
const actual =
await importOriginal<typeof import('@qwen-code/qwen-code-core')>();
return {
...actual,
IdeClient: {
getInstance: vi.fn().mockResolvedValue({
getDetectedIdeDisplayName: vi.fn().mockReturnValue('test-ide'),
}),
},
};
});
vi.mock('../../utils/version.js', () => ({
getCliVersion: vi.fn(),
}));
vi.mock('../../utils/systemInfo.js');
describe('aboutCommand', () => {
let mockContext: CommandContext;
const originalPlatform = process.platform;
const originalEnv = { ...process.env };
beforeEach(() => {
mockContext = createMockCommandContext({
services: {
config: {
getModel: vi.fn(),
getModel: vi.fn().mockReturnValue('test-model'),
getIdeMode: vi.fn().mockReturnValue(true),
getSessionId: vi.fn().mockReturnValue('test-session-id'),
},
settings: {
merged: {
@@ -56,21 +40,25 @@ describe('aboutCommand', () => {
},
} as unknown as CommandContext);
vi.mocked(versionUtils.getCliVersion).mockResolvedValue('test-version');
vi.spyOn(mockContext.services.config!, 'getModel').mockReturnValue(
'test-model',
);
process.env['GOOGLE_CLOUD_PROJECT'] = 'test-gcp-project';
Object.defineProperty(process, 'platform', {
value: 'test-os',
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
});
});
afterEach(() => {
vi.unstubAllEnvs();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
process.env = originalEnv;
vi.clearAllMocks();
});
@@ -81,30 +69,55 @@ describe('aboutCommand', () => {
});
it('should call addItem with all version info', async () => {
process.env['SANDBOX'] = '';
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
await aboutCommand.action(mockContext, '');
expect(systemInfoUtils.getExtendedSystemInfo).toHaveBeenCalledWith(
mockContext,
);
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
expect.objectContaining({
type: MessageType.ABOUT,
cliVersion: 'test-version',
osVersion: 'test-os',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
gcpProject: 'test-gcp-project',
ideClient: 'test-ide',
},
systemInfo: expect.objectContaining({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
}),
}),
expect.any(Number),
);
});
it('should show the correct sandbox environment variable', async () => {
process.env['SANDBOX'] = 'gemini-sandbox';
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'gemini-sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
});
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
@@ -113,15 +126,32 @@ describe('aboutCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
sandboxEnv: 'gemini-sandbox',
type: MessageType.ABOUT,
systemInfo: expect.objectContaining({
sandboxEnv: 'gemini-sandbox',
}),
}),
expect.any(Number),
);
});
it('should show sandbox-exec profile when applicable', async () => {
process.env['SANDBOX'] = 'sandbox-exec';
process.env['SEATBELT_PROFILE'] = 'test-profile';
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'sandbox-exec (test-profile)',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
});
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
@@ -130,18 +160,31 @@ describe('aboutCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
sandboxEnv: 'sandbox-exec (test-profile)',
systemInfo: expect.objectContaining({
sandboxEnv: 'sandbox-exec (test-profile)',
}),
}),
expect.any(Number),
);
});
it('should not show ide client when it is not detected', async () => {
vi.mocked(IdeClient.getInstance).mockResolvedValue({
getDetectedIdeDisplayName: vi.fn().mockReturnValue(undefined),
} as unknown as IdeClient);
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: '',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
});
process.env['SANDBOX'] = '';
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
@@ -151,13 +194,87 @@ describe('aboutCommand', () => {
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageType.ABOUT,
cliVersion: 'test-version',
osVersion: 'test-os',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
gcpProject: 'test-gcp-project',
ideClient: '',
systemInfo: expect.objectContaining({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: '',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
}),
}),
expect.any(Number),
);
});
it('should show unknown npmVersion when npm command fails', async () => {
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: 'unknown',
sandboxEnv: 'no sandbox',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
});
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
await aboutCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
systemInfo: expect.objectContaining({
npmVersion: 'unknown',
}),
}),
expect.any(Number),
);
});
it('should show unknown sessionId when config is not available', async () => {
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: 'test-version',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'no sandbox',
modelVersion: 'Unknown',
selectedAuthType: 'test-auth',
ideClient: '',
sessionId: 'unknown',
memoryUsage: '100 MB',
baseUrl: undefined,
});
if (!aboutCommand.action) {
throw new Error('The about command must have an action.');
}
await aboutCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
systemInfo: expect.objectContaining({
sessionId: 'unknown',
}),
}),
expect.any(Number),
);

View File

@@ -4,53 +4,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { getCliVersion } from '../../utils/version.js';
import type { CommandContext, SlashCommand } from './types.js';
import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';
import process from 'node:process';
import { MessageType, type HistoryItemAbout } from '../types.js';
import { IdeClient } from '@qwen-code/qwen-code-core';
import { getExtendedSystemInfo } from '../../utils/systemInfo.js';
export const aboutCommand: SlashCommand = {
name: 'about',
description: 'show version info',
kind: CommandKind.BUILT_IN,
action: async (context) => {
const osVersion = process.platform;
let sandboxEnv = 'no sandbox';
if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') {
sandboxEnv = process.env['SANDBOX'];
} else if (process.env['SANDBOX'] === 'sandbox-exec') {
sandboxEnv = `sandbox-exec (${
process.env['SEATBELT_PROFILE'] || 'unknown'
})`;
}
const modelVersion = context.services.config?.getModel() || 'Unknown';
const cliVersion = await getCliVersion();
const selectedAuthType =
context.services.settings.merged.security?.auth?.selectedType || '';
const gcpProject = process.env['GOOGLE_CLOUD_PROJECT'] || '';
const ideClient = await getIdeClientName(context);
const systemInfo = await getExtendedSystemInfo(context);
const aboutItem: Omit<HistoryItemAbout, 'id'> = {
type: MessageType.ABOUT,
cliVersion,
osVersion,
sandboxEnv,
modelVersion,
selectedAuthType,
gcpProject,
ideClient,
systemInfo,
};
context.ui.addItem(aboutItem, Date.now());
},
};
async function getIdeClientName(context: CommandContext) {
if (!context.services.config?.getIdeMode()) {
return '';
}
const ideClient = await IdeClient.getInstance();
return ideClient?.getDetectedIdeDisplayName() ?? '';
}

View File

@@ -8,41 +8,34 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import open from 'open';
import { bugCommand } from './bugCommand.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { getCliVersion } from '../../utils/version.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
import { formatMemoryUsage } from '../utils/formatters.js';
import { AuthType } from '@qwen-code/qwen-code-core';
import * as systemInfoUtils from '../../utils/systemInfo.js';
// Mock dependencies
vi.mock('open');
vi.mock('../../utils/version.js');
vi.mock('../utils/formatters.js');
vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {
const actual =
await importOriginal<typeof import('@qwen-code/qwen-code-core')>();
return {
...actual,
IdeClient: {
getInstance: () => ({
getDetectedIdeDisplayName: vi.fn().mockReturnValue('VSCode'),
}),
},
};
});
vi.mock('node:process', () => ({
default: {
platform: 'test-platform',
version: 'v20.0.0',
// Keep other necessary process properties if needed by other parts of the code
env: process.env,
memoryUsage: () => ({ rss: 0 }),
},
}));
vi.mock('../../utils/systemInfo.js');
describe('bugCommand', () => {
beforeEach(() => {
vi.mocked(getCliVersion).mockResolvedValue('0.1.0');
vi.mocked(formatMemoryUsage).mockReturnValue('100 MB');
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: '0.1.0',
osPlatform: 'test-platform',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'test',
modelVersion: 'qwen3-coder-plus',
selectedAuthType: '',
ideClient: 'VSCode',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
gitCommit:
GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)
? GIT_COMMIT_INFO
: undefined,
});
vi.stubEnv('SANDBOX', 'qwen-test');
});
@@ -55,19 +48,7 @@ describe('bugCommand', () => {
const mockContext = createMockCommandContext({
services: {
config: {
getModel: () => 'qwen3-coder-plus',
getBugCommand: () => undefined,
getIdeMode: () => true,
getSessionId: () => 'test-session-id',
},
settings: {
merged: {
security: {
auth: {
selectedType: undefined,
},
},
},
},
},
});
@@ -75,14 +56,21 @@ describe('bugCommand', () => {
if (!bugCommand.action) throw new Error('Action is not defined');
await bugCommand.action(mockContext, 'A test bug');
const gitCommitLine =
GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)
? `* **Git Commit:** ${GIT_COMMIT_INFO}\n`
: '';
const expectedInfo = `
* **CLI Version:** 0.1.0
* **Git Commit:** ${GIT_COMMIT_INFO}
${gitCommitLine}* **Model:** qwen3-coder-plus
* **Sandbox:** test
* **OS Platform:** test-platform
* **OS Arch:** x64
* **OS Release:** 22.0.0
* **Node.js Version:** v20.0.0
* **NPM Version:** 10.0.0
* **Session ID:** test-session-id
* **Operating System:** test-platform v20.0.0
* **Sandbox Environment:** test
* **Auth Type:**
* **Model Version:** qwen3-coder-plus
* **Auth Method:**
* **Memory Usage:** 100 MB
* **IDE Client:** VSCode
`;
@@ -99,19 +87,7 @@ describe('bugCommand', () => {
const mockContext = createMockCommandContext({
services: {
config: {
getModel: () => 'qwen3-coder-plus',
getBugCommand: () => ({ urlTemplate: customTemplate }),
getIdeMode: () => true,
getSessionId: () => 'test-session-id',
},
settings: {
merged: {
security: {
auth: {
selectedType: undefined,
},
},
},
},
},
});
@@ -119,14 +95,21 @@ describe('bugCommand', () => {
if (!bugCommand.action) throw new Error('Action is not defined');
await bugCommand.action(mockContext, 'A custom bug');
const gitCommitLine =
GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)
? `* **Git Commit:** ${GIT_COMMIT_INFO}\n`
: '';
const expectedInfo = `
* **CLI Version:** 0.1.0
* **Git Commit:** ${GIT_COMMIT_INFO}
${gitCommitLine}* **Model:** qwen3-coder-plus
* **Sandbox:** test
* **OS Platform:** test-platform
* **OS Arch:** x64
* **OS Release:** 22.0.0
* **Node.js Version:** v20.0.0
* **NPM Version:** 10.0.0
* **Session ID:** test-session-id
* **Operating System:** test-platform v20.0.0
* **Sandbox Environment:** test
* **Auth Type:**
* **Model Version:** qwen3-coder-plus
* **Auth Method:**
* **Memory Usage:** 100 MB
* **IDE Client:** VSCode
`;
@@ -138,25 +121,30 @@ describe('bugCommand', () => {
});
it('should include Base URL when auth type is OpenAI', async () => {
vi.mocked(systemInfoUtils.getExtendedSystemInfo).mockResolvedValue({
cliVersion: '0.1.0',
osPlatform: 'test-platform',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'test',
modelVersion: 'qwen3-coder-plus',
selectedAuthType: AuthType.USE_OPENAI,
ideClient: 'VSCode',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: 'https://api.openai.com/v1',
gitCommit:
GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)
? GIT_COMMIT_INFO
: undefined,
});
const mockContext = createMockCommandContext({
services: {
config: {
getModel: () => 'qwen3-coder-plus',
getBugCommand: () => undefined,
getIdeMode: () => true,
getSessionId: () => 'test-session-id',
getContentGeneratorConfig: () => ({
baseUrl: 'https://api.openai.com/v1',
}),
},
settings: {
merged: {
security: {
auth: {
selectedType: AuthType.USE_OPENAI,
},
},
},
},
},
});
@@ -164,15 +152,22 @@ describe('bugCommand', () => {
if (!bugCommand.action) throw new Error('Action is not defined');
await bugCommand.action(mockContext, 'OpenAI bug');
const gitCommitLine =
GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)
? `* **Git Commit:** ${GIT_COMMIT_INFO}\n`
: '';
const expectedInfo = `
* **CLI Version:** 0.1.0
* **Git Commit:** ${GIT_COMMIT_INFO}
${gitCommitLine}* **Model:** qwen3-coder-plus
* **Sandbox:** test
* **OS Platform:** test-platform
* **OS Arch:** x64
* **OS Release:** 22.0.0
* **Node.js Version:** v20.0.0
* **NPM Version:** 10.0.0
* **Session ID:** test-session-id
* **Operating System:** test-platform v20.0.0
* **Sandbox Environment:** test
* **Auth Type:** ${AuthType.USE_OPENAI}
* **Auth Method:** ${AuthType.USE_OPENAI}
* **Base URL:** https://api.openai.com/v1
* **Model Version:** qwen3-coder-plus
* **Memory Usage:** 100 MB
* **IDE Client:** VSCode
`;

View File

@@ -5,17 +5,17 @@
*/
import open from 'open';
import process from 'node:process';
import {
type CommandContext,
type SlashCommand,
CommandKind,
} from './types.js';
import { MessageType } from '../types.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
import { formatMemoryUsage } from '../utils/formatters.js';
import { getCliVersion } from '../../utils/version.js';
import { IdeClient, AuthType } from '@qwen-code/qwen-code-core';
import { getExtendedSystemInfo } from '../../utils/systemInfo.js';
import {
getSystemInfoFields,
getFieldValue,
} from '../../utils/systemInfoFields.js';
export const bugCommand: SlashCommand = {
name: 'bug',
@@ -23,50 +23,20 @@ export const bugCommand: SlashCommand = {
kind: CommandKind.BUILT_IN,
action: async (context: CommandContext, args?: string): Promise<void> => {
const bugDescription = (args || '').trim();
const { config } = context.services;
const systemInfo = await getExtendedSystemInfo(context);
const osVersion = `${process.platform} ${process.version}`;
let sandboxEnv = 'no sandbox';
if (process.env['SANDBOX'] && process.env['SANDBOX'] !== 'sandbox-exec') {
sandboxEnv = process.env['SANDBOX'].replace(/^qwen-(?:code-)?/, '');
} else if (process.env['SANDBOX'] === 'sandbox-exec') {
sandboxEnv = `sandbox-exec (${
process.env['SEATBELT_PROFILE'] || 'unknown'
})`;
}
const modelVersion = config?.getModel() || 'Unknown';
const cliVersion = await getCliVersion();
const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);
const ideClient = await getIdeClientName(context);
const selectedAuthType =
context.services.settings.merged.security?.auth?.selectedType || '';
const baseUrl =
selectedAuthType === AuthType.USE_OPENAI
? config?.getContentGeneratorConfig()?.baseUrl
: undefined;
const fields = getSystemInfoFields(systemInfo);
let info = `
* **CLI Version:** ${cliVersion}
* **Git Commit:** ${GIT_COMMIT_INFO}
* **Session ID:** ${config?.getSessionId() || 'unknown'}
* **Operating System:** ${osVersion}
* **Sandbox Environment:** ${sandboxEnv}
* **Auth Type:** ${selectedAuthType}`;
if (baseUrl) {
info += `\n* **Base URL:** ${baseUrl}`;
}
info += `
* **Model Version:** ${modelVersion}
* **Memory Usage:** ${memoryUsage}
`;
if (ideClient) {
info += `* **IDE Client:** ${ideClient}\n`;
// Generate bug report info using the same field configuration
let info = '\n';
for (const field of fields) {
info += `* **${field.label}:** ${getFieldValue(field, systemInfo)}\n`;
}
let bugReportUrl =
'https://github.com/QwenLM/qwen-code/issues/new?template=bug_report.yml&title={title}&info={info}';
const bugCommandSettings = config?.getBugCommand();
const bugCommandSettings = context.services.config?.getBugCommand();
if (bugCommandSettings?.urlTemplate) {
bugReportUrl = bugCommandSettings.urlTemplate;
}
@@ -98,11 +68,3 @@ export const bugCommand: SlashCommand = {
}
},
};
async function getIdeClientName(context: CommandContext) {
if (!context.services.config?.getIdeMode()) {
return '';
}
const ideClient = await IdeClient.getInstance();
return ideClient.getDetectedIdeDisplayName() ?? '';
}

View File

@@ -7,127 +7,46 @@
import type React from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
import type { ExtendedSystemInfo } from '../../utils/systemInfo.js';
import {
getSystemInfoFields,
getFieldValue,
type SystemInfoField,
} from '../../utils/systemInfoFields.js';
interface AboutBoxProps {
cliVersion: string;
osVersion: string;
sandboxEnv: string;
modelVersion: string;
selectedAuthType: string;
gcpProject: string;
ideClient: string;
}
type AboutBoxProps = ExtendedSystemInfo;
export const AboutBox: React.FC<AboutBoxProps> = ({
cliVersion,
osVersion,
sandboxEnv,
modelVersion,
selectedAuthType,
gcpProject,
ideClient,
}) => (
<Box
borderStyle="round"
borderColor={theme.border.default}
flexDirection="column"
padding={1}
marginY={1}
width="100%"
>
<Box marginBottom={1}>
<Text bold color={theme.text.accent}>
About Qwen Code
</Text>
</Box>
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
CLI Version
export const AboutBox: React.FC<AboutBoxProps> = (props) => {
const fields = getSystemInfoFields(props);
return (
<Box
borderStyle="round"
borderColor={theme.border.default}
flexDirection="column"
padding={1}
marginY={1}
width="100%"
>
<Box marginBottom={1}>
<Text bold color={theme.text.accent}>
About Qwen Code
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{cliVersion}</Text>
</Box>
{fields.map((field: SystemInfoField) => (
<Box key={field.key} flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
{field.label}
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>
{getFieldValue(field, props)}
</Text>
</Box>
</Box>
))}
</Box>
{GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO) && (
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
Git Commit
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{GIT_COMMIT_INFO}</Text>
</Box>
</Box>
)}
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
Model
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{modelVersion}</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
Sandbox
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{sandboxEnv}</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
OS
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{osVersion}</Text>
</Box>
</Box>
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
Auth Method
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>
{selectedAuthType.startsWith('oauth') ? 'OAuth' : selectedAuthType}
</Text>
</Box>
</Box>
{gcpProject && (
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
GCP Project
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{gcpProject}</Text>
</Box>
</Box>
)}
{ideClient && (
<Box flexDirection="row">
<Box width="35%">
<Text bold color={theme.text.link}>
IDE Client
</Text>
</Box>
<Box>
<Text color={theme.text.primary}>{ideClient}</Text>
</Box>
</Box>
)}
</Box>
);
);
};

View File

@@ -71,15 +71,24 @@ describe('<HistoryItemDisplay />', () => {
it('renders AboutBox for "about" type', () => {
const item: HistoryItem = {
...baseItem,
id: 1,
type: MessageType.ABOUT,
cliVersion: '1.0.0',
osVersion: 'test-os',
sandboxEnv: 'test-env',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
gcpProject: 'test-project',
ideClient: 'test-ide',
systemInfo: {
cliVersion: '1.0.0',
osPlatform: 'test-os',
osArch: 'x64',
osRelease: '22.0.0',
nodeVersion: 'v20.0.0',
npmVersion: '10.0.0',
sandboxEnv: 'test-env',
modelVersion: 'test-model',
selectedAuthType: 'test-auth',
ideClient: 'test-ide',
sessionId: 'test-session-id',
memoryUsage: '100 MB',
baseUrl: undefined,
gitCommit: undefined,
},
};
const { lastFrame } = renderWithProviders(
<HistoryItemDisplay {...baseItem} item={item} />,

View File

@@ -95,15 +95,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
<ErrorMessage text={itemForDisplay.text} />
)}
{itemForDisplay.type === 'about' && (
<AboutBox
cliVersion={itemForDisplay.cliVersion}
osVersion={itemForDisplay.osVersion}
sandboxEnv={itemForDisplay.sandboxEnv}
modelVersion={itemForDisplay.modelVersion}
selectedAuthType={itemForDisplay.selectedAuthType}
gcpProject={itemForDisplay.gcpProject}
ideClient={itemForDisplay.ideClient}
/>
<AboutBox {...itemForDisplay.systemInfo} />
)}
{itemForDisplay.type === 'help' && commands && (
<Help commands={commands} />

View File

@@ -138,13 +138,7 @@ export const useSlashCommandProcessor = (
if (message.type === MessageType.ABOUT) {
historyItemContent = {
type: 'about',
cliVersion: message.cliVersion,
osVersion: message.osVersion,
sandboxEnv: message.sandboxEnv,
modelVersion: message.modelVersion,
selectedAuthType: message.selectedAuthType,
gcpProject: message.gcpProject,
ideClient: message.ideClient,
systemInfo: message.systemInfo,
};
} else if (message.type === MessageType.HELP) {
historyItemContent = {

View File

@@ -120,13 +120,22 @@ export type HistoryItemWarning = HistoryItemBase & {
export type HistoryItemAbout = HistoryItemBase & {
type: 'about';
cliVersion: string;
osVersion: string;
sandboxEnv: string;
modelVersion: string;
selectedAuthType: string;
gcpProject: string;
ideClient: string;
systemInfo: {
cliVersion: string;
osPlatform: string;
osArch: string;
osRelease: string;
nodeVersion: string;
npmVersion: string;
sandboxEnv: string;
modelVersion: string;
selectedAuthType: string;
ideClient: string;
sessionId: string;
memoryUsage: string;
baseUrl?: string;
gitCommit?: string;
};
};
export type HistoryItemHelp = HistoryItemBase & {
@@ -288,13 +297,22 @@ export type Message =
| {
type: MessageType.ABOUT;
timestamp: Date;
cliVersion: string;
osVersion: string;
sandboxEnv: string;
modelVersion: string;
selectedAuthType: string;
gcpProject: string;
ideClient: string;
systemInfo: {
cliVersion: string;
osPlatform: string;
osArch: string;
osRelease: string;
nodeVersion: string;
npmVersion: string;
sandboxEnv: string;
modelVersion: string;
selectedAuthType: string;
ideClient: string;
sessionId: string;
memoryUsage: string;
baseUrl?: string;
gitCommit?: string;
};
content?: string; // Optional content, not really used for ABOUT
}
| {