fix: remove monorepo

This commit is contained in:
Sam Denty 2024-09-25 19:54:09 +01:00
parent d364a6f774
commit 6fb59d2bc5
No known key found for this signature in database
GPG Key ID: 7B4EAF7B9E291B79
137 changed files with 194 additions and 1229 deletions

View File

@ -45,7 +45,6 @@ jobs:
with:
wranglerVersion: '* -w'
packageManager: pnpm
workingDirectory: 'packages/bolt'
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy

View File

@ -1,55 +1,84 @@
# Bolt Monorepo
# Bolt
Welcome to the Bolt monorepo! This repository contains the codebase for Bolt, an AI assistant developed by StackBlitz.
Bolt is an AI assistant developed by StackBlitz. This package contains the UI interface for Bolt as well as the server components, built using [Remix Run](https://remix.run/).
## Repository Structure
## Prerequisites
Currently, this monorepo contains a single package:
- [`bolt`](packages/bolt): The main package containing the UI interface for Bolt as well as the server components.
As the project grows, additional packages may be added to this workspace.
## Getting Started
### Prerequisites
Before you begin, ensure you have the following installed:
- Node.js (v20.15.1)
- pnpm (v9.4.0)
### Installation
## Setup
1. Clone the repository:
1. Clone the repository (if you haven't already):
```bash
git clone https://github.com/stackblitz/bolt.git
cd bolt
```
2. Install dependencies:
```bash
pnpm i
pnpm install
```
3. Optionally, init git hooks:
3. Create a `.env.local` file in the root directory and add your Anthropic API key:
```bash
pnpmx husky
```
ANTHROPIC_API_KEY=XXX
```
### Development
Optionally, you an set the debug level or disable authentication:
To start developing the Bolt UI:
1. Navigate to the bolt package:
```bash
cd packages/bolt
```
VITE_LOG_LEVEL=debug
VITE_DISABLE_AUTH=1
```
2. Start the development server:
If you want to run authentication against a local StackBlitz instance, add:
```
VITE_CLIENT_ORIGIN=https://local.stackblitz.com:3000
```
**Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore.
## Available Scripts
- `pnpm run dev`: Starts the development server.
- `pnpm run build`: Builds the project.
- `pnpm run start`: Runs the built application locally using Wrangler Pages. This script uses `bindings.sh` to set up necessary bindings so you don't have to duplicate environment variables.
- `pnpm run preview`: Builds the project and then starts it locally, useful for testing the production build. Note, HTTP streaming currently doesn't work as expected with `wrangler pages dev`.
- `pnpm test:` Runs the test suite using Vitest.
- `pnpm run typecheck`: Runs TypeScript type checking.
- `pnpm run typegen`: Generates TypeScript types using Wrangler.
- `pnpm run deploy`: Builds the project and deploys it to Cloudflare Pages.
## Development
To start the development server:
```bash
pnpm run dev
```
This will start the Remix Vite development server.
## Testing
Run the test suite with:
```bash
pnpm test
```
## Deployment
To deploy the application to Cloudflare Pages:
```bash
pnpm run deploy
```
Make sure you have the necessary permissions and Wrangler is correctly configured for your Cloudflare account.

View File

@ -4,7 +4,6 @@ import { useChat } from 'ai/react';
import { useAnimate } from 'framer-motion';
import { memo, useEffect, useRef, useState } from 'react';
import { cssTransition, toast, ToastContainer } from 'react-toastify';
import { AnalyticsAction, AnalyticsTrackEvent, sendAnalyticsEvent } from '~/lib/analytics';
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
import { useChatHistory } from '~/lib/persistence';
import { chatStore } from '~/lib/stores/chat';
@ -195,18 +194,6 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
resetEnhancer();
textareaRef.current?.blur();
const event = messages.length === 0 ? AnalyticsTrackEvent.ChatCreated : AnalyticsTrackEvent.MessageSent;
sendAnalyticsEvent({
action: AnalyticsAction.Track,
payload: {
event,
properties: {
message: _input,
},
},
});
};
const [messageRef, scrollRef] = useSnapScroll();

View File

@ -2,7 +2,6 @@ import { useStore } from '@nanostores/react';
import { chatStore } from '~/lib/stores/chat';
import { workbenchStore } from '~/lib/stores/workbench';
import { classNames } from '~/utils/classNames';
import { OpenStackBlitz } from './OpenStackBlitz.client';
interface HeaderActionButtonsProps {}
@ -40,9 +39,6 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
<div className="i-ph:code-bold" />
</Button>
</div>
<div className="flex ml-2">
<OpenStackBlitz />
</div>
</div>
);
}

38
app/lib/analytics.ts Normal file
View File

@ -0,0 +1,38 @@
import { CLIENT_ORIGIN } from '~/lib/constants';
import { request as doRequest } from '~/lib/fetch';
export interface Identity {
userId?: string | null;
guestId?: string | null;
segmentWriteKey?: string | null;
avatar?: string;
}
const MESSAGE_PREFIX = 'Bolt';
export enum AnalyticsTrackEvent {
MessageSent = `${MESSAGE_PREFIX} Message Sent`,
MessageComplete = `${MESSAGE_PREFIX} Message Complete`,
ChatCreated = `${MESSAGE_PREFIX} Chat Created`,
}
export async function identifyUser(access: string): Promise<Identity | undefined> {
const response = await doRequest(`${CLIENT_ORIGIN}/api/identify`, {
method: 'GET',
headers: { authorization: `Bearer ${access}` },
});
const body = await response.json();
if (!response.ok) {
return undefined;
}
// convert numerical identity values to strings
const stringified = Object.entries(body).map(([key, value]) => [
key,
typeof value === 'number' ? value.toString() : value,
]);
return Object.fromEntries(stringified) as Identity;
}

View File

@ -3,7 +3,6 @@ import { useState, useEffect } from 'react';
import { atom } from 'nanostores';
import type { Message } from 'ai';
import { toast } from 'react-toastify';
import { AnalyticsAction, sendAnalyticsEvent } from '~/lib/analytics';
import { workbenchStore } from '~/lib/stores/workbench';
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
@ -107,14 +106,4 @@ function navigateChat(nextId: string) {
url.pathname = `/chat/${nextId}`;
window.history.replaceState({}, '', url);
// since the `replaceState` call doesn't trigger a page reload, we need to manually log this event
sendAnalyticsEvent({
action: AnalyticsAction.Page,
payload: {
properties: {
url: url.href,
},
},
});
}

View File

@ -1,9 +1,7 @@
import { useStore } from '@nanostores/react';
import type { LinksFunction } from '@remix-run/cloudflare';
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLocation } from '@remix-run/react';
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
import tailwindReset from '@unocss/reset/tailwind-compat.css?url';
import { useEffect } from 'react';
import { sendAnalyticsEvent, AnalyticsAction } from './lib/analytics';
import { themeStore } from './lib/stores/theme';
import { stripIndents } from './utils/stripIndent';
@ -55,20 +53,6 @@ const inlineThemeCode = stripIndents`
export function Layout({ children }: { children: React.ReactNode }) {
const theme = useStore(themeStore);
const { pathname } = useLocation();
// log page events when the window location changes
useEffect(() => {
sendAnalyticsEvent({
action: AnalyticsAction.Page,
payload: {
properties: {
url: window.location.href,
},
},
});
}, [pathname]);
return (
<html lang="en" data-theme={theme}>
<head>

View File

@ -4,14 +4,12 @@ import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants';
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
import SwitchableStream from '~/lib/.server/llm/switchable-stream';
import type { Session } from '~/lib/.server/sessions';
import { AnalyticsAction, AnalyticsTrackEvent, sendEventInternal } from '~/lib/analytics';
export async function action(args: ActionFunctionArgs) {
return actionWithAuth(args, chatAction);
}
async function chatAction({ context, request }: ActionFunctionArgs, session: Session) {
async function chatAction({ context, request }: ActionFunctionArgs) {
const { messages } = await request.json<{ messages: Messages }>();
const stream = new SwitchableStream();
@ -19,19 +17,8 @@ async function chatAction({ context, request }: ActionFunctionArgs, session: Ses
try {
const options: StreamingOptions = {
toolChoice: 'none',
onFinish: async ({ text: content, finishReason, usage }) => {
onFinish: async ({ text: content, finishReason }) => {
if (finishReason !== 'length') {
await sendEventInternal(session, {
action: AnalyticsAction.Track,
payload: {
event: AnalyticsTrackEvent.MessageComplete,
properties: {
usage,
finishReason,
},
},
});
return stream.close();
}

Some files were not shown because too many files have changed in this diff Show More