mirror of
https://github.com/coleam00/bolt.new-any-llm
synced 2024-12-28 06:42:56 +00:00
fix: remove monorepo
This commit is contained in:
parent
d364a6f774
commit
6fb59d2bc5
1
.github/workflows/ci.yaml
vendored
1
.github/workflows/ci.yaml
vendored
@ -45,7 +45,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
wranglerVersion: '* -w'
|
wranglerVersion: '* -w'
|
||||||
packageManager: pnpm
|
packageManager: pnpm
|
||||||
workingDirectory: 'packages/bolt'
|
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
command: pages deploy
|
command: pages deploy
|
||||||
|
83
README.md
83
README.md
@ -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:
|
Before you begin, ensure you have the following installed:
|
||||||
|
|
||||||
- [`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
|
|
||||||
|
|
||||||
- Node.js (v20.15.1)
|
- Node.js (v20.15.1)
|
||||||
- pnpm (v9.4.0)
|
- pnpm (v9.4.0)
|
||||||
|
|
||||||
### Installation
|
## Setup
|
||||||
|
|
||||||
1. Clone the repository:
|
1. Clone the repository (if you haven't already):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/stackblitz/bolt.git
|
git clone https://github.com/stackblitz/bolt.git
|
||||||
cd bolt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install dependencies:
|
2. Install dependencies:
|
||||||
|
|
||||||
```bash
|
```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:
|
```
|
||||||
|
VITE_LOG_LEVEL=debug
|
||||||
1. Navigate to the bolt package:
|
VITE_DISABLE_AUTH=1
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/bolt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
```bash
|
||||||
pnpm run dev
|
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.
|
||||||
|
@ -4,7 +4,6 @@ import { useChat } from 'ai/react';
|
|||||||
import { useAnimate } from 'framer-motion';
|
import { useAnimate } from 'framer-motion';
|
||||||
import { memo, useEffect, useRef, useState } from 'react';
|
import { memo, useEffect, useRef, useState } from 'react';
|
||||||
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
||||||
import { AnalyticsAction, AnalyticsTrackEvent, sendAnalyticsEvent } from '~/lib/analytics';
|
|
||||||
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
||||||
import { useChatHistory } from '~/lib/persistence';
|
import { useChatHistory } from '~/lib/persistence';
|
||||||
import { chatStore } from '~/lib/stores/chat';
|
import { chatStore } from '~/lib/stores/chat';
|
||||||
@ -195,18 +194,6 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
|
|||||||
resetEnhancer();
|
resetEnhancer();
|
||||||
|
|
||||||
textareaRef.current?.blur();
|
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();
|
const [messageRef, scrollRef] = useSnapScroll();
|
@ -2,7 +2,6 @@ import { useStore } from '@nanostores/react';
|
|||||||
import { chatStore } from '~/lib/stores/chat';
|
import { chatStore } from '~/lib/stores/chat';
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
import { OpenStackBlitz } from './OpenStackBlitz.client';
|
|
||||||
|
|
||||||
interface HeaderActionButtonsProps {}
|
interface HeaderActionButtonsProps {}
|
||||||
|
|
||||||
@ -40,9 +39,6 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
|
|||||||
<div className="i-ph:code-bold" />
|
<div className="i-ph:code-bold" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex ml-2">
|
|
||||||
<OpenStackBlitz />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
38
app/lib/analytics.ts
Normal file
38
app/lib/analytics.ts
Normal 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;
|
||||||
|
}
|
@ -3,7 +3,6 @@ import { useState, useEffect } from 'react';
|
|||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { AnalyticsAction, sendAnalyticsEvent } from '~/lib/analytics';
|
|
||||||
import { workbenchStore } from '~/lib/stores/workbench';
|
import { workbenchStore } from '~/lib/stores/workbench';
|
||||||
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
|
import { getMessages, getNextId, getUrlId, openDatabase, setMessages } from './db';
|
||||||
|
|
||||||
@ -107,14 +106,4 @@ function navigateChat(nextId: string) {
|
|||||||
url.pathname = `/chat/${nextId}`;
|
url.pathname = `/chat/${nextId}`;
|
||||||
|
|
||||||
window.history.replaceState({}, '', url);
|
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
@ -1,9 +1,7 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import type { LinksFunction } from '@remix-run/cloudflare';
|
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 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 { themeStore } from './lib/stores/theme';
|
||||||
import { stripIndents } from './utils/stripIndent';
|
import { stripIndents } from './utils/stripIndent';
|
||||||
|
|
||||||
@ -55,20 +53,6 @@ const inlineThemeCode = stripIndents`
|
|||||||
export function Layout({ children }: { children: React.ReactNode }) {
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
const theme = useStore(themeStore);
|
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 (
|
return (
|
||||||
<html lang="en" data-theme={theme}>
|
<html lang="en" data-theme={theme}>
|
||||||
<head>
|
<head>
|
@ -4,14 +4,12 @@ import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants';
|
|||||||
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
|
import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts';
|
||||||
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
|
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
|
||||||
import SwitchableStream from '~/lib/.server/llm/switchable-stream';
|
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) {
|
export async function action(args: ActionFunctionArgs) {
|
||||||
return actionWithAuth(args, chatAction);
|
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 { messages } = await request.json<{ messages: Messages }>();
|
||||||
|
|
||||||
const stream = new SwitchableStream();
|
const stream = new SwitchableStream();
|
||||||
@ -19,19 +17,8 @@ async function chatAction({ context, request }: ActionFunctionArgs, session: Ses
|
|||||||
try {
|
try {
|
||||||
const options: StreamingOptions = {
|
const options: StreamingOptions = {
|
||||||
toolChoice: 'none',
|
toolChoice: 'none',
|
||||||
onFinish: async ({ text: content, finishReason, usage }) => {
|
onFinish: async ({ text: content, finishReason }) => {
|
||||||
if (finishReason !== 'length') {
|
if (finishReason !== 'length') {
|
||||||
await sendEventInternal(session, {
|
|
||||||
action: AnalyticsAction.Track,
|
|
||||||
payload: {
|
|
||||||
event: AnalyticsTrackEvent.MessageComplete,
|
|
||||||
properties: {
|
|
||||||
usage,
|
|
||||||
finishReason,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return stream.close();
|
return stream.close();
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user