chore: checkpoint existing workspace changes

This commit is contained in:
777genius 2026-05-23 13:48:35 +03:00
parent 74fea9c176
commit 64fdfd2422
8 changed files with 193 additions and 50 deletions

View file

@ -84,6 +84,14 @@ const faqIcons = [
<style scoped>
.faq-section {
position: relative;
--faq-title-gradient: linear-gradient(135deg, #e0e6ff 0%, #ffd700 100%);
--faq-subtitle-color: #8892b0;
--faq-panel-bg: rgba(10, 10, 15, 0.8);
--faq-panel-border: rgba(0, 240, 255, 0.08);
--faq-panel-question: #e0e6ff;
--faq-panel-answer: #8892b0;
--faq-panel-hover-shadow: 0 8px 32px rgba(0, 240, 255, 0.06);
--faq-panel-hover-border: rgba(0, 240, 255, 0.2);
}
.faq-section__header {
@ -100,7 +108,7 @@ const faqIcons = [
letter-spacing: -0.03em;
line-height: 1.15;
margin-bottom: 16px;
background: linear-gradient(135deg, #e0e6ff 0%, #ffd700 100%);
background: var(--faq-title-gradient);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
@ -108,7 +116,7 @@ const faqIcons = [
.faq-section__subtitle {
font-size: 1.1rem;
color: #8892b0;
color: var(--faq-subtitle-color);
line-height: 1.6;
margin: 0;
}
@ -134,9 +142,10 @@ const faqIcons = [
.faq-section__panel {
border-radius: 16px !important;
background: rgba(10, 10, 15, 0.8) !important;
border: 1px solid rgba(0, 240, 255, 0.08) !important;
background: var(--faq-panel-bg) !important;
border: 1px solid var(--faq-panel-border) !important;
backdrop-filter: blur(12px);
color: var(--faq-panel-question) !important;
transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
overflow: hidden;
animation: faqFadeIn 0.5s ease both;
@ -145,8 +154,8 @@ const faqIcons = [
.faq-section__panel:hover {
transform: translateY(-2px);
border-color: rgba(0, 240, 255, 0.2) !important;
box-shadow: 0 8px 32px rgba(0, 240, 255, 0.06);
border-color: var(--faq-panel-hover-border) !important;
box-shadow: var(--faq-panel-hover-shadow);
}
.faq-section__panel::after {
@ -160,6 +169,8 @@ const faqIcons = [
.faq-section__panel-title {
padding: 20px 24px !important;
min-height: unset !important;
background: transparent !important;
color: var(--faq-panel-question) !important;
}
:deep(.faq-section__panel-title .v-expansion-panel-title__overlay) {
@ -198,17 +209,18 @@ const faqIcons = [
font-size: 1rem;
font-weight: 600;
line-height: 1.4;
color: #e0e6ff;
color: var(--faq-panel-question);
}
:deep(.faq-section__panel-text .v-expansion-panel-text__wrapper) {
padding: 0 24px 20px 82px !important;
background: transparent !important;
}
.faq-section__answer {
font-size: 0.95rem;
line-height: 1.7;
color: #8892b0;
color: var(--faq-panel-answer);
}
.faq-section__answer :deep(a) {
@ -300,32 +312,28 @@ const faqIcons = [
}
/* Light Theme */
.v-theme--light .faq-section__title {
background: linear-gradient(135deg, #1e293b 0%, #d97706 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
:global(.v-theme--light) .faq-section,
:global(html.light) .faq-section {
--faq-title-gradient: linear-gradient(135deg, #1e293b 0%, #d97706 100%);
--faq-subtitle-color: #475569;
--faq-panel-bg: rgba(255, 255, 255, 0.75);
--faq-panel-border: rgba(0, 0, 0, 0.06);
--faq-panel-question: #1e293b;
--faq-panel-answer: #475569;
--faq-panel-hover-shadow: 0 8px 32px rgba(0, 180, 200, 0.08);
--faq-panel-hover-border: rgba(0, 240, 255, 0.2);
}
.v-theme--light .faq-section__subtitle {
color: #475569;
}
.v-theme--light .faq-section__panel {
background: rgba(255, 255, 255, 0.75) !important;
border-color: rgba(0, 0, 0, 0.06) !important;
}
.v-theme--light .faq-section__panel:hover {
box-shadow: 0 8px 32px rgba(0, 180, 200, 0.08);
}
.v-theme--light .faq-section__panel-question {
color: #1e293b;
}
.v-theme--light .faq-section__answer {
color: #475569;
:global(.v-theme--dark) .faq-section,
:global(html.dark) .faq-section {
--faq-title-gradient: linear-gradient(135deg, #e0e6ff 0%, #ffd700 100%);
--faq-subtitle-color: #8892b0;
--faq-panel-bg: rgba(10, 10, 15, 0.8);
--faq-panel-border: rgba(0, 240, 255, 0.08);
--faq-panel-question: #e0e6ff;
--faq-panel-answer: #8892b0;
--faq-panel-hover-shadow: 0 8px 32px rgba(0, 240, 255, 0.06);
--faq-panel-hover-border: rgba(0, 240, 255, 0.2);
}
@media (max-width: 960px) {

View file

@ -42,6 +42,10 @@ export const useBrowserTheme = () => {
const applyDocumentTheme = (name: ThemeName) => {
if (!import.meta.client) return;
document.documentElement.classList.toggle("dark", name === "dark");
document.documentElement.classList.toggle("light", name === "light");
document.documentElement.style.colorScheme = name;
document.querySelectorAll(".v-application").forEach((app) => {
app.classList.toggle("v-theme--dark", name === "dark");
app.classList.toggle("v-theme--light", name === "light");

View file

@ -311,6 +311,7 @@
"target": [
"nsis"
],
"executableName": "AgentTeamsAI",
"icon": "resources/icons/win/icon.ico"
},
"linux": {
@ -348,7 +349,8 @@
"artifactName": "Agent.Teams.AI.Setup.${version}.${ext}",
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true
"allowToChangeInstallationDirectory": true,
"shortcutName": "Agent Teams AI"
},
"publish": [
{

View file

@ -92,9 +92,7 @@ function shouldKeepNodePtyPrebuild(entryName, platform, archLabel) {
if (platform === 'darwin' && archLabel === 'universal') {
return (
entryName === 'darwin-universal' ||
entryName === 'darwin-arm64' ||
entryName === 'darwin-x64'
entryName === 'darwin-universal' || entryName === 'darwin-arm64' || entryName === 'darwin-x64'
);
}
@ -154,11 +152,85 @@ async function pruneNodePtyArtifacts(appOutDir, platform, archLabel) {
removedPaths.push(absolutePath);
}
}
const hasTargetPrebuild = await hasNodePtyTargetPrebuild(nodePtyRoot, platform, archLabel);
if (!hasTargetPrebuild) {
continue;
}
for (const buildName of ['Release', 'Debug']) {
const buildDir = path.join(nodePtyRoot, 'build', buildName);
if (!(await directoryExists(buildDir))) {
continue;
}
if (await containsIncompatibleNativeBinary(buildDir, platform, archLabel)) {
await fs.promises.rm(buildDir, { recursive: true, force: true });
removedPaths.push(buildDir);
}
}
}
return removedPaths;
}
async function directoryExists(dirPath) {
try {
const stat = await fs.promises.stat(dirPath);
return stat.isDirectory();
} catch {
return false;
}
}
async function hasNodePtyTargetPrebuild(nodePtyRoot, platform, archLabel) {
const prebuildsDir = path.join(nodePtyRoot, 'prebuilds');
let entries;
try {
entries = await fs.promises.readdir(prebuildsDir, { withFileTypes: true });
} catch {
return false;
}
for (const entry of entries) {
if (!entry.isDirectory() || !shouldKeepNodePtyPrebuild(entry.name, platform, archLabel)) {
continue;
}
const ptyNativePath = path.join(prebuildsDir, entry.name, 'pty.node');
if (await fileExists(ptyNativePath)) {
return true;
}
}
return false;
}
async function fileExists(filePath) {
try {
const stat = await fs.promises.stat(filePath);
return stat.isFile();
} catch {
return false;
}
}
async function containsIncompatibleNativeBinary(rootDir, targetPlatform, targetArch) {
const files = await walkFiles(rootDir);
for (const filePath of files) {
const metadata = await detectBinaryMetadata(filePath);
if (!metadata) {
continue;
}
if (!isBinaryCompatible(metadata.format, metadata.archs, targetPlatform, targetArch)) {
return true;
}
}
return false;
}
function findNodeModulesSequence(segments, sequence) {
for (let index = 0; index <= segments.length - sequence.length; index += 1) {
let matches = true;
@ -332,12 +404,7 @@ function parseElf(buffer) {
if (buffer.length < 20) {
return null;
}
if (
buffer[0] !== 0x7f ||
buffer[1] !== 0x45 ||
buffer[2] !== 0x4c ||
buffer[3] !== 0x46
) {
if (buffer[0] !== 0x7f || buffer[1] !== 0x45 || buffer[2] !== 0x4c || buffer[3] !== 0x46) {
return null;
}
@ -454,11 +521,7 @@ async function afterPack(context) {
const removedPaths = [
...(await pruneNodePtyArtifacts(context.appOutDir, targetPlatform, targetArch)),
...(await pruneKnownIncompatibleNativeArtifacts(
context.appOutDir,
targetPlatform,
targetArch
)),
...(await pruneKnownIncompatibleNativeArtifacts(context.appOutDir, targetPlatform, targetArch)),
];
const mismatches = await validateNativeBinaries(context.appOutDir, targetPlatform, targetArch);

View file

@ -30,6 +30,7 @@ import type { ChildProcessWithoutNullStreams } from 'child_process';
const PROBE_COMMAND_TIMEOUT_MS = 90_000;
const COMMAND_TIMEOUT_MS = PROBE_COMMAND_TIMEOUT_MS;
const COMMAND_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
const COMMAND_ERROR_DETAIL_LIMIT = 1_600;
const COMMAND_OUTPUT_PREVIEW_LIMIT = 1_200;
const ESCAPE_CHARACTER = String.fromCharCode(27);
@ -870,8 +871,12 @@ function appendOptionalArg(args: string[], name: string, value: string | null |
function runtimeProviderCommandOptions<T extends { env: NodeJS.ProcessEnv }>(
options: T,
projectPath: string | null
): T & { cwd?: string } {
return projectPath ? { ...options, cwd: projectPath } : options;
): T & { cwd?: string; maxBuffer: number } {
const commandOptions = {
...options,
maxBuffer: COMMAND_MAX_BUFFER_BYTES,
};
return projectPath ? { ...commandOptions, cwd: projectPath } : commandOptions;
}
async function resolveCliEnv(): Promise<{

View file

@ -13,6 +13,7 @@ import {
SelectValue,
} from '@renderer/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@renderer/components/ui/tabs';
import { cn } from '@renderer/lib/utils';
import {
compareOpenCodeTeamModelRecommendations,
getOpenCodeTeamModelRecommendation,
@ -819,7 +820,10 @@ const RuntimeProviderErrorAlert = ({
type="button"
size="sm"
variant="ghost"
className="h-6 shrink-0 px-2 text-[11px]"
className={cn(
'h-6 shrink-0 px-2 text-[11px]',
!copied && 'member-launch-diagnostics-pulse'
)}
title={copied ? 'Diagnostics copied' : 'Copy diagnostics'}
aria-label={copied ? 'Diagnostics copied' : 'Copy diagnostics'}
onClick={(event) => {

View file

@ -134,6 +134,62 @@ describe('electron-builder afterPack', () => {
expect(fs.existsSync(path.join(prebuildsDir, 'darwin-x64'))).toBe(false);
});
it('prunes host node-pty build outputs when a matching target prebuild exists', async () => {
const tempDir = createTempDir();
tempDirs.push(tempDir);
const nodePtyDir = path.join(
tempDir,
'resources',
'app.asar.unpacked',
'node_modules',
'node-pty'
);
const releaseDir = path.join(nodePtyDir, 'build', 'Release');
const prebuildDir = path.join(nodePtyDir, 'prebuilds', 'win32-x64');
writeFile(path.join(tempDir, 'AgentTeamsAI.exe'), createPortableExecutableBuffer('x64'));
writeFile(path.join(releaseDir, 'pty.node'), createMachOBuffer('arm64'));
writeFile(path.join(releaseDir, 'spawn-helper'), createMachOBuffer('arm64'));
writeFile(path.join(prebuildDir, 'pty.node'), createPortableExecutableBuffer('x64'));
writeFile(path.join(prebuildDir, 'conpty.node'), createPortableExecutableBuffer('x64'));
await afterPackModule({
appOutDir: tempDir,
electronPlatformName: 'win32',
arch: 1,
});
expect(fs.existsSync(releaseDir)).toBe(false);
expect(fs.existsSync(path.join(prebuildDir, 'pty.node'))).toBe(true);
});
it('keeps incompatible node-pty build outputs when no target prebuild exists', async () => {
const tempDir = createTempDir();
tempDirs.push(tempDir);
const releaseDir = path.join(
tempDir,
'resources',
'app.asar.unpacked',
'node_modules',
'node-pty',
'build',
'Release'
);
writeFile(path.join(tempDir, 'agent-teams-ai'), createElfBuffer('x64'));
writeFile(path.join(releaseDir, 'pty.node'), createMachOBuffer('arm64'));
await expect(
afterPackModule({
appOutDir: tempDir,
electronPlatformName: 'linux',
arch: 1,
})
).rejects.toThrow('Found incompatible native binaries in linux-x64 bundle after pruning');
expect(fs.existsSync(releaseDir)).toBe(true);
});
it('fails validation when a foreign-arch native binary remains in the bundle', async () => {
const tempDir = createTempDir();
tempDirs.push(tempDir);

View file

@ -1380,6 +1380,7 @@ describe('AgentTeamsRuntimeProviderManagementCliClient', () => {
],
expect.objectContaining({ cwd: '/Users/test/project' })
);
expect(execCliMock.mock.calls[0]?.[2]).toMatchObject({ maxBuffer: 8 * 1024 * 1024 });
expect(JSON.stringify(execCliMock.mock.calls[0])).not.toContain('undefined');
});