Skip to content

Commit 70ff9c7

Browse files
github-actions[bot]chasprowebdevMarfuen
authored
fix(portal): update module to download executable device agent file for windows (#1766)
Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
1 parent 21488c1 commit 70ff9c7

File tree

2 files changed

+20
-99
lines changed

2 files changed

+20
-99
lines changed

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

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
99
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@comp/ui/tooltip';
1010
import type { Member } from '@db';
1111
import { CheckCircle2, Circle, Download, HelpCircle, Loader2, XCircle } from 'lucide-react';
12-
import Image from 'next/image';
1312
import { useEffect, useMemo, useState } from 'react';
1413
import { toast } from 'sonner';
1514
import type { FleetPolicy, Host } from '../../types';
@@ -43,7 +42,10 @@ export function DeviceAgentAccordionItem({
4342

4443
const hasInstalledAgent = host !== null;
4544
const failedPoliciesCount = useMemo(() => {
46-
return fleetPolicies.filter((policy) => policy.response !== 'pass').length + (!isMacOS || mdmEnabledStatus.response === 'pass' ? 0 : 1);
45+
return (
46+
fleetPolicies.filter((policy) => policy.response !== 'pass').length +
47+
(!isMacOS || mdmEnabledStatus.response === 'pass' ? 0 : 1)
48+
);
4749
}, [fleetPolicies, mdmEnabledStatus, isMacOS]);
4850

4951
const isCompleted = hasInstalledAgent && failedPoliciesCount === 0;
@@ -84,7 +86,7 @@ export function DeviceAgentAccordionItem({
8486
? 'Comp AI Agent-1.0.0-arm64.dmg'
8587
: 'Comp AI Agent-1.0.0-intel.dmg';
8688
} else {
87-
a.download = 'compai-device-agent.zip';
89+
a.download = 'Comp AI Agent 1.0.0.exe';
8890
}
8991

9092
document.body.appendChild(a);
@@ -189,35 +191,13 @@ export function DeviceAgentAccordionItem({
189191
</Button>
190192
</div>
191193
</li>
192-
{!isMacOS && (
193-
<li>
194-
<strong>Run the "Install Me First" file</strong>
195-
<p className="mt-1">
196-
After extracting the downloaded zip file, locate and run the "Install Me
197-
First" file to prepare your system.
198-
</p>
199-
</li>
200-
)}
201194
<li>
202-
<strong>
203-
{isMacOS
204-
? 'Install the Comp AI Device Agent'
205-
: 'Run the Comp AI Device Agent installer'}
206-
</strong>
195+
<strong>Install the Comp AI Device Agent</strong>
207196
<p className="mt-1">
208197
{isMacOS
209198
? 'Double-click the downloaded DMG file and follow the installation instructions.'
210-
: 'Follow the installation wizard steps. When you reach the introduction screen (as shown below), click "Continue" to proceed through the installation.'}
199+
: 'Double-click the downloaded EXE file and follow the installation instructions.'}
211200
</p>
212-
{!isMacOS && (
213-
<Image
214-
src="/osquery-agent.jpeg"
215-
alt="Fleet osquery installer introduction screen"
216-
width={600}
217-
height={400}
218-
className="mt-2 rounded-xs border"
219-
/>
220-
)}
221201
</li>
222202
{isMacOS ? (
223203
<li>
@@ -283,7 +263,9 @@ export function DeviceAgentAccordionItem({
283263
<div
284264
className={cn(
285265
'hover:bg-muted/50 flex items-center justify-between rounded-md border border-l-4 p-3 shadow-sm transition-colors',
286-
mdmEnabledStatus.response === 'pass' ? 'border-l-green-500' : 'border-l-red-500',
266+
mdmEnabledStatus.response === 'pass'
267+
? 'border-l-green-500'
268+
: 'border-l-red-500',
287269
)}
288270
>
289271
<div className="flex items-center gap-2">

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

Lines changed: 10 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,8 @@ import { logger } from '@/utils/logger';
22
import { s3Client } from '@/utils/s3';
33
import { GetObjectCommand } from '@aws-sdk/client-s3';
44
import { client as kv } from '@comp/kv';
5-
import archiver from 'archiver';
65
import { type NextRequest, NextResponse } from 'next/server';
7-
import { PassThrough, Readable } from 'stream';
8-
import {
9-
generateWindowsScript,
10-
getPackageFilename,
11-
getReadmeContent,
12-
getScriptFilename,
13-
} from './scripts';
14-
import type { SupportedOS } from './types';
6+
import { Readable } from 'stream';
157

168
export const runtime = 'nodejs';
179
export const dynamic = 'force-dynamic';
@@ -41,15 +33,7 @@ export async function GET(req: NextRequest) {
4133
// Delete token after retrieval (one-time use)
4234
await kv.del(`download:${token}`);
4335

44-
const { orgId, employeeId } = downloadInfo as {
45-
orgId: string;
46-
employeeId: string;
47-
userId: string;
48-
};
49-
5036
// Hardcoded device marker paths used by the setup scripts
51-
const fleetDevicePathMac = '/Users/Shared/.fleet';
52-
const fleetDevicePathWindows = 'C:\\ProgramData\\CompAI\\Fleet';
5337
const fleetBucketName = process.env.FLEET_AGENT_BUCKET_NAME;
5438

5539
if (!fleetBucketName) {
@@ -94,44 +78,9 @@ export async function GET(req: NextRequest) {
9478
}
9579
}
9680

97-
// Windows flow: Generate script and create zip
98-
const fleetDevicePath = fleetDevicePathWindows;
99-
const script = generateWindowsScript({ orgId, employeeId, fleetDevicePath });
100-
81+
// Windows flow: Generate script and create zip const fleetDevicePath = fleetDevicePathWindows;
10182
try {
102-
// Create a passthrough stream for the response
103-
const passThrough = new PassThrough();
104-
const archive = archiver('zip', { zlib: { level: 9 } });
105-
106-
// Pipe archive to passthrough
107-
archive.pipe(passThrough);
108-
109-
// Robust error handling for staging/prod reliability
110-
archive.on('error', (err) => {
111-
logger('archiver_error', { message: err?.message, stack: (err as Error)?.stack });
112-
passThrough.destroy(err as Error);
113-
});
114-
archive.on('warning', (warn) => {
115-
logger('archiver_warning', { message: (warn as Error)?.message });
116-
});
117-
passThrough.on('error', (err) => {
118-
logger('download_stream_error', {
119-
message: (err as Error)?.message,
120-
stack: (err as Error)?.stack,
121-
});
122-
});
123-
124-
// Add script file
125-
const scriptFilename = getScriptFilename(os as SupportedOS);
126-
archive.append(script, { name: scriptFilename, mode: 0o755 });
127-
128-
// Add README
129-
const readmeContent = getReadmeContent(os as SupportedOS);
130-
archive.append(readmeContent, { name: 'README.txt' });
131-
132-
// Get package from S3 and stream it
133-
const packageFilename = getPackageFilename(os as SupportedOS);
134-
const windowsPackageFilename = 'fleet-osquery.msi';
83+
const windowsPackageFilename = 'Comp AI Agent 1.0.0.exe';
13584
const packageKey = `windows/${windowsPackageFilename}`;
13685

13786
const getObjectCommand = new GetObjectCommand({
@@ -141,29 +90,19 @@ export async function GET(req: NextRequest) {
14190

14291
const s3Response = await s3Client.send(getObjectCommand);
14392

144-
if (s3Response.Body) {
145-
const s3Stream = s3Response.Body as Readable;
146-
s3Stream.on('error', (err) => {
147-
logger('s3_stream_error', {
148-
message: (err as Error)?.message,
149-
stack: (err as Error)?.stack,
150-
});
151-
passThrough.destroy(err as Error);
152-
});
153-
archive.append(s3Stream, { name: packageFilename, store: true });
93+
if (!s3Response.Body) {
94+
return new NextResponse('Executable file not found', { status: 404 });
15495
}
15596

156-
// Finalize the archive
157-
archive.finalize();
158-
159-
// Convert Node.js stream to Web Stream for NextResponse
160-
const webStream = Readable.toWeb(passThrough) as unknown as ReadableStream;
97+
// Convert S3 stream to Web Stream for NextResponse
98+
const s3Stream = s3Response.Body as Readable;
99+
const webStream = Readable.toWeb(s3Stream) as unknown as ReadableStream;
161100

162101
// Return streaming response with headers that trigger browser download
163102
return new NextResponse(webStream, {
164103
headers: {
165-
'Content-Type': 'application/zip',
166-
'Content-Disposition': `attachment; filename="compai-device-agent-${os}.zip"`,
104+
'Content-Type': 'application/octet-stream',
105+
'Content-Disposition': `attachment; filename="${windowsPackageFilename}"`,
167106
'Cache-Control': 'no-cache, no-store, must-revalidate',
168107
'X-Accel-Buffering': 'no',
169108
},

0 commit comments

Comments
 (0)