Skip to content

Commit ba76ef1

Browse files
committed
chore: merge main into release for new releases
2 parents 383f67d + dbc3d0b commit ba76ef1

File tree

5 files changed

+32
-150
lines changed

5 files changed

+32
-150
lines changed

apps/api/Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@ RUN curl -fsSL https://bun.sh/install | bash \
1515
&& export PATH="/root/.bun/bin:$PATH" \
1616
&& bun install --production --ignore-scripts || true
1717

18-
# Copy pre-built workspace packages (must be before copying app contents)
1918
COPY node_modules/@trycompai ./node_modules/@trycompai
19+
COPY node_modules/@prisma ./node_modules/@prisma
20+
COPY node_modules/.prisma ./node_modules/.prisma
2021

21-
# Now copy the pre-built app contents (dist/, prisma/, etc.)
2222
COPY . .
2323

24-
# Generate Prisma client inside the image (ensures runtime client matches installed deps)
25-
RUN npx prisma generate
26-
2724
# Set environment variables
2825
ENV NODE_ENV=production
2926
ENV PORT=3333

apps/api/buildspec.yml

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,67 +32,48 @@ phases:
3232
- echo "Installing API dependencies only..."
3333
- bun install --filter=@comp/api --frozen-lockfile || bun install --filter=@comp/api --ignore-scripts || bun install --ignore-scripts
3434

35-
# Build email package (required dependency for API)
3635
- echo "Building @trycompai/email package..."
3736
- cd packages/email
3837
- bun run build
3938
- cd ../..
4039

41-
# Build NestJS application (prebuild automatically handles Prisma)
4240
- echo "Building NestJS application..."
4341
- echo "APP_NAME is set to $APP_NAME"
4442
- echo "Current directory $(pwd)"
4543
- echo "Available apps $(ls -la apps/)"
4644
- cd apps/api
4745
- echo "Changed to $(pwd)"
48-
- echo "Running build (includes automatic prebuild db:generate)..."
4946
- bun run build
5047

51-
# Verify build output exists
5248
- echo "Checking build output..."
5349
- ls -la dist/
5450
- ls -la dist/src/
5551
- '[ -f "dist/src/main.js" ] || { echo "❌ main.js not found in dist/src"; exit 1; }'
5652

57-
# Create self-contained bundle for Docker (stay in apps/api)
58-
- echo "Creating self-contained bundle..."
5953
- mkdir -p ../docker-build
60-
61-
# Copy built application (preserves NestJS structure)
62-
- echo "Copying built application..."
6354
- cp -r dist/* ../docker-build/
64-
65-
# Copy prisma folder (needed for runtime imports)
66-
- echo "Copying prisma folder..."
6755
- cp -r prisma ../docker-build/
6856

69-
# Verify files were copied correctly
7057
- echo "Verifying copied files..."
7158
- ls -la ../docker-build/
7259
- ls -la ../docker-build/src/
7360
- '[ -f "../docker-build/src/main.js" ] || { echo "❌ main.js not found in docker-build/src"; exit 1; }'
7461

75-
# Copy entire node_modules for runtime (includes @trycompai/db from npm)
76-
- echo "Skipping host node_modules copy; Dockerfile installs prod deps inside image"
77-
78-
# Copy built workspace packages (needed for runtime)
79-
# Only copy packages that are actually imported at runtime
80-
# Email package is already bundled, so only need its dist
81-
- echo "Copying built workspace packages..."
8262
- mkdir -p ../docker-build/node_modules/@trycompai
83-
# Email package - copy dist and package.json (needed at runtime)
8463
- mkdir -p ../docker-build/node_modules/@trycompai/email
8564
- cp -r ../../packages/email/dist ../docker-build/node_modules/@trycompai/email/
8665
- cp ../../packages/email/package.json ../docker-build/node_modules/@trycompai/email/
66+
- mkdir -p ../docker-build/node_modules/@trycompai/utils
67+
- cp -r ../../packages/utils/src ../docker-build/node_modules/@trycompai/utils/
68+
- cp ../../packages/utils/package.json ../docker-build/node_modules/@trycompai/utils/
69+
70+
- cp -r ../../node_modules/@prisma ../docker-build/node_modules/@prisma
71+
- cp -r ../../node_modules/.prisma ../docker-build/node_modules/.prisma
8772

88-
# Copy Dockerfile
89-
- echo "Copying Dockerfile..."
9073
- cp Dockerfile ../docker-build/
91-
- echo "Copying package manifests for image install..."
9274
- cp package.json ../docker-build/
9375
- cp ../../bun.lock ../docker-build/ || true
9476

95-
# Build Docker image
9677
- echo "Building Docker image..."
9778
- docker build --build-arg BUILDKIT_INLINE_CACHE=1 -t $ECR_REPOSITORY_URI:$IMAGE_TAG ../docker-build/
9879
- docker tag $ECR_REPOSITORY_URI:$IMAGE_TAG $ECR_REPOSITORY_URI:latest

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)