Skip to content

Commit ebff53e

Browse files
committed
feat: enhance DeviceAgentAccordionItem for macOS support and update download handling
- Added OS detection to customize download file names and instructions for macOS users. - Updated the download route to serve a DMG file directly for macOS, while maintaining zip file generation for Windows. - Improved user guidance in the UI for installation steps based on the operating system.
1 parent 9a5ab4f commit ebff53e

2 files changed

Lines changed: 74 additions & 29 deletions

File tree

apps/portal/src/app/(app)/(home)/[orgId]/components/tasks/DeviceAgentAccordionItem.tsx

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export function DeviceAgentAccordionItem({
2424
}: DeviceAgentAccordionItemProps) {
2525
const [isDownloading, setIsDownloading] = useState(false);
2626

27+
// Detect OS from user agent
28+
const isMacOS = typeof window !== 'undefined' && navigator.userAgent.includes('Mac');
29+
2730
const hasInstalledAgent = host !== null;
2831
const allPoliciesPass =
2932
fleetPolicies.length === 0 || fleetPolicies.every((policy) => policy.response === 'pass');
@@ -57,7 +60,7 @@ export function DeviceAgentAccordionItem({
5760
// Method 1: Using a temporary link (most reliable)
5861
const a = document.createElement('a');
5962
a.href = downloadUrl;
60-
a.download = 'compai-device-agent.zip';
63+
a.download = isMacOS ? 'Comp AI Agent-1.0.0-arm64.dmg' : 'compai-device-agent.zip';
6164
document.body.appendChild(a);
6265
a.click();
6366
document.body.removeChild(a);
@@ -136,33 +139,43 @@ export function DeviceAgentAccordionItem({
136139
{getButtonContent()}
137140
</Button>
138141
</li>
142+
{!isMacOS && (
143+
<li>
144+
<strong>Run the "Install Me First" file</strong>
145+
<p className="mt-1">
146+
After extracting the downloaded zip file, locate and run the "Install Me
147+
First" file to prepare your system.
148+
</p>
149+
</li>
150+
)}
139151
<li>
140-
<strong>Run the "Install Me First" file</strong>
141-
<p className="mt-1">
142-
After extracting the downloaded zip file, locate and run the "Install Me First"
143-
file to prepare your system.
144-
</p>
145-
</li>
146-
<li>
147-
<strong>Run the Comp AI Device Agent installer</strong>
152+
<strong>
153+
{isMacOS
154+
? 'Install the Comp AI Device Agent'
155+
: 'Run the Comp AI Device Agent installer'}
156+
</strong>
148157
<p className="mt-1">
149-
Follow the installation wizard steps. When you reach the introduction screen (as
150-
shown below), click "Continue" to proceed through the installation.
158+
{isMacOS
159+
? 'Double-click the downloaded DMG file and follow the installation instructions.'
160+
: 'Follow the installation wizard steps. When you reach the introduction screen (as shown below), click "Continue" to proceed through the installation.'}
151161
</p>
152-
<Image
153-
src="/osquery-agent.jpeg"
154-
alt="Fleet osquery installer introduction screen"
155-
width={600}
156-
height={400}
157-
className="mt-2 rounded-xs border"
158-
/>
162+
{!isMacOS && (
163+
<Image
164+
src="/osquery-agent.jpeg"
165+
alt="Fleet osquery installer introduction screen"
166+
width={600}
167+
height={400}
168+
className="mt-2 rounded-xs border"
169+
/>
170+
)}
159171
</li>
160172
<li>
161173
<strong>Enable MDM</strong>
162174
<div className="space-y-2">
163175
<p>
164-
On Mac, on the top of your screen, find the Fleet Desktop app which looks like
165-
an F made of dots. Click on it and click My Device.
176+
{isMacOS
177+
? 'On the top of your screen, find the Fleet Desktop app which looks like an F made of dots. Click on it and click My Device.'
178+
: 'Find the Fleet Desktop app in your system tray (bottom right corner). Click on it and click My Device.'}
166179
</p>
167180
<p>
168181
You should see a banner that asks you to enable MDM. Click the button and

apps/portal/src/app/api/download-agent/route.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import archiver from 'archiver';
66
import { type NextRequest, NextResponse } from 'next/server';
77
import { PassThrough, Readable } from 'stream';
88
import {
9-
generateMacScript,
109
generateWindowsScript,
1110
getPackageFilename,
1211
getReadmeContent,
@@ -52,12 +51,46 @@ export async function GET(req: NextRequest) {
5251
return new NextResponse('Server configuration error', { status: 500 });
5352
}
5453

55-
// Generate OS-specific script
56-
const fleetDevicePath = os === 'macos' ? fleetDevicePathMac : fleetDevicePathWindows;
57-
const script =
58-
os === 'macos'
59-
? generateMacScript({ orgId, employeeId, fleetDevicePath })
60-
: generateWindowsScript({ orgId, employeeId, fleetDevicePath });
54+
// For macOS, serve the DMG directly. For Windows, create a zip with script and installer.
55+
if (os === 'macos') {
56+
try {
57+
// Direct DMG download for macOS
58+
const macosPackageFilename = 'Comp AI Agent-1.0.0-arm64.dmg';
59+
const packageKey = `macos/${macosPackageFilename}`;
60+
61+
const getObjectCommand = new GetObjectCommand({
62+
Bucket: fleetBucketName,
63+
Key: packageKey,
64+
});
65+
66+
const s3Response = await s3Client.send(getObjectCommand);
67+
68+
if (!s3Response.Body) {
69+
return new NextResponse('DMG file not found', { status: 404 });
70+
}
71+
72+
// Convert S3 stream to Web Stream for NextResponse
73+
const s3Stream = s3Response.Body as Readable;
74+
const webStream = Readable.toWeb(s3Stream) as unknown as ReadableStream;
75+
76+
// Return streaming response with headers that trigger browser download
77+
return new NextResponse(webStream, {
78+
headers: {
79+
'Content-Type': 'application/x-apple-diskimage',
80+
'Content-Disposition': `attachment; filename="${macosPackageFilename}"`,
81+
'Cache-Control': 'no-cache, no-store, must-revalidate',
82+
'X-Accel-Buffering': 'no',
83+
},
84+
});
85+
} catch (error) {
86+
logger('Error downloading macOS DMG', { error });
87+
return new NextResponse('Failed to download macOS agent', { status: 500 });
88+
}
89+
}
90+
91+
// Windows flow: Generate script and create zip
92+
const fleetDevicePath = fleetDevicePathWindows;
93+
const script = generateWindowsScript({ orgId, employeeId, fleetDevicePath });
6194

6295
try {
6396
// Create a passthrough stream for the response
@@ -92,9 +125,8 @@ export async function GET(req: NextRequest) {
92125

93126
// Get package from S3 and stream it
94127
const packageFilename = getPackageFilename(os);
95-
const macosPackageFilename = 'Comp AI Agent-1.0.0-arm64.dmg';
96128
const windowsPackageFilename = 'fleet-osquery.msi';
97-
const packageKey = `${os}/${os === 'macos' ? macosPackageFilename : windowsPackageFilename}`;
129+
const packageKey = `windows/${windowsPackageFilename}`;
98130

99131
const getObjectCommand = new GetObjectCommand({
100132
Bucket: fleetBucketName,

0 commit comments

Comments
 (0)