Compare commits
12 Commits
437d110e37
...
9d8c4055ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d8c4055ca | ||
|
|
3f6050b227 | ||
|
|
5f925566c4 | ||
|
|
983b3025a5 | ||
|
|
49850d9253 | ||
|
|
1a55bd5866 | ||
|
|
f138b9c088 | ||
|
|
4e37f5a80c | ||
|
|
d34852c227 | ||
|
|
cb3c536c5d | ||
|
|
b32c4081ec | ||
|
|
583dfbda53 |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -74,7 +74,7 @@ jobs:
|
||||
- name: Validate Docker production build
|
||||
run: |
|
||||
echo "🐳 Testing Docker production target..."
|
||||
docker build --target runtime . --no-cache --progress=plain
|
||||
docker build --target bolt-ai-production . --no-cache --progress=plain
|
||||
echo "✅ Production target builds successfully"
|
||||
|
||||
- name: Validate Docker development build
|
||||
|
||||
2
.github/workflows/docker.yaml
vendored
2
.github/workflows/docker.yaml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
target: runtime
|
||||
target: bolt-ai-production
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
83
Dockerfile
83
Dockerfile
@@ -9,6 +9,10 @@ ENV CI=true
|
||||
# Use pnpm
|
||||
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
|
||||
|
||||
# Ensure git is available for build and runtime scripts
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Accept (optional) build-time public URL for Remix/Vite (Coolify can pass it)
|
||||
ARG VITE_PUBLIC_APP_URL
|
||||
ENV VITE_PUBLIC_APP_URL=${VITE_PUBLIC_APP_URL}
|
||||
@@ -25,68 +29,75 @@ RUN pnpm install --offline --frozen-lockfile
|
||||
# Build the Remix app (SSR + client)
|
||||
RUN NODE_OPTIONS=--max-old-space-size=4096 pnpm run build
|
||||
|
||||
# ---- production dependencies stage ----
|
||||
FROM build AS prod-deps
|
||||
|
||||
# Keep only production deps for runtime
|
||||
RUN pnpm prune --prod --ignore-scripts
|
||||
|
||||
|
||||
# ---- runtime stage ----
|
||||
FROM node:22-bookworm-slim AS runtime
|
||||
# ---- production stage ----
|
||||
FROM prod-deps AS bolt-ai-production
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV PORT=5173
|
||||
ENV HOST=0.0.0.0
|
||||
|
||||
# Install curl so Coolify’s healthcheck works inside the image
|
||||
# Non-sensitive build arguments
|
||||
ARG VITE_LOG_LEVEL=debug
|
||||
ARG DEFAULT_NUM_CTX
|
||||
|
||||
# Set non-sensitive environment variables
|
||||
ENV WRANGLER_SEND_METRICS=false \
|
||||
VITE_LOG_LEVEL=${VITE_LOG_LEVEL} \
|
||||
DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX} \
|
||||
RUNNING_IN_DOCKER=true
|
||||
|
||||
# Note: API keys should be provided at runtime via docker run -e or docker-compose
|
||||
# Example: docker run -e OPENAI_API_KEY=your_key_here ...
|
||||
|
||||
# Install curl for healthchecks and copy bindings script
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy only what we need to run
|
||||
COPY --from=build /app/build /app/build
|
||||
COPY --from=build /app/node_modules /app/node_modules
|
||||
COPY --from=build /app/package.json /app/package.json
|
||||
# Copy built files and scripts
|
||||
COPY --from=prod-deps /app/build /app/build
|
||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||
COPY --from=prod-deps /app/package.json /app/package.json
|
||||
COPY --from=prod-deps /app/bindings.sh /app/bindings.sh
|
||||
|
||||
EXPOSE 3000
|
||||
# Pre-configure wrangler to disable metrics
|
||||
RUN mkdir -p /root/.config/.wrangler && \
|
||||
echo '{"enabled":false}' > /root/.config/.wrangler/metrics.json
|
||||
|
||||
# Healthcheck for Coolify
|
||||
# Make bindings script executable
|
||||
RUN chmod +x /app/bindings.sh
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
# Healthcheck for deployment platforms
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=5 \
|
||||
CMD curl -fsS http://localhost:3000/ || exit 1
|
||||
CMD curl -fsS http://localhost:5173/ || exit 1
|
||||
|
||||
# Start the Remix server
|
||||
CMD ["node", "build/server/index.js"]
|
||||
# Start using dockerstart script with Wrangler
|
||||
CMD ["pnpm", "run", "dockerstart"]
|
||||
|
||||
|
||||
# ---- development stage ----
|
||||
FROM build AS development
|
||||
|
||||
# Define environment variables for development
|
||||
ARG GROQ_API_KEY
|
||||
ARG HuggingFace_API_KEY
|
||||
ARG OPENAI_API_KEY
|
||||
ARG ANTHROPIC_API_KEY
|
||||
ARG OPEN_ROUTER_API_KEY
|
||||
ARG GOOGLE_GENERATIVE_AI_API_KEY
|
||||
ARG OLLAMA_API_BASE_URL
|
||||
ARG XAI_API_KEY
|
||||
ARG TOGETHER_API_KEY
|
||||
ARG TOGETHER_API_BASE_URL
|
||||
# Non-sensitive development arguments
|
||||
ARG VITE_LOG_LEVEL=debug
|
||||
ARG DEFAULT_NUM_CTX
|
||||
|
||||
ENV GROQ_API_KEY=${GROQ_API_KEY} \
|
||||
HuggingFace_API_KEY=${HuggingFace_API_KEY} \
|
||||
OPENAI_API_KEY=${OPENAI_API_KEY} \
|
||||
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} \
|
||||
OPEN_ROUTER_API_KEY=${OPEN_ROUTER_API_KEY} \
|
||||
GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} \
|
||||
OLLAMA_API_BASE_URL=${OLLAMA_API_BASE_URL} \
|
||||
XAI_API_KEY=${XAI_API_KEY} \
|
||||
TOGETHER_API_KEY=${TOGETHER_API_KEY} \
|
||||
TOGETHER_API_BASE_URL=${TOGETHER_API_BASE_URL} \
|
||||
AWS_BEDROCK_CONFIG=${AWS_BEDROCK_CONFIG} \
|
||||
VITE_LOG_LEVEL=${VITE_LOG_LEVEL} \
|
||||
# Set non-sensitive environment variables for development
|
||||
ENV VITE_LOG_LEVEL=${VITE_LOG_LEVEL} \
|
||||
DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX} \
|
||||
RUNNING_IN_DOCKER=true
|
||||
|
||||
# Note: API keys should be provided at runtime via docker run -e or docker-compose
|
||||
# Example: docker run -e OPENAI_API_KEY=your_key_here ...
|
||||
|
||||
RUN mkdir -p /app/run
|
||||
CMD ["pnpm", "run", "dev", "--host"]
|
||||
|
||||
41
README.md
41
README.md
@@ -154,29 +154,52 @@ You have two options for running Bolt.DIY: directly on your machine or using Doc
|
||||
|
||||
### Option 2: Using Docker
|
||||
|
||||
This option requires some familiarity with Docker but provides a more isolated environment.
|
||||
This option requires Docker and is great when you want an isolated environment or to mirror the production image.
|
||||
|
||||
#### Additional Prerequisite
|
||||
|
||||
- Install Docker: [Download Docker](https://www.docker.com/)
|
||||
|
||||
#### Steps:
|
||||
#### Steps
|
||||
|
||||
1. **Build the Docker Image**:
|
||||
1. **Prepare Environment Variables**
|
||||
|
||||
Copy the provided examples and add your provider keys:
|
||||
|
||||
```bash
|
||||
# Using npm script:
|
||||
npm run dockerbuild
|
||||
|
||||
# OR using direct Docker command:
|
||||
docker build . --target bolt-ai-development
|
||||
cp .env.example .env
|
||||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
2. **Run the Container**:
|
||||
The runtime scripts inside the container source `.env` and `.env.local`, so keep any API keys you need in one of those files.
|
||||
|
||||
2. **Build an Image**
|
||||
|
||||
```bash
|
||||
# Development image (bind-mounts your local source when run)
|
||||
pnpm run dockerbuild
|
||||
# ≈ docker build -t bolt-ai:development -t bolt-ai:latest --target development .
|
||||
|
||||
# Production image (self-contained build artifacts)
|
||||
pnpm run dockerbuild:prod
|
||||
# ≈ docker build -t bolt-ai:production -t bolt-ai:latest --target bolt-ai-production .
|
||||
```
|
||||
|
||||
3. **Run the Container**
|
||||
|
||||
```bash
|
||||
# Development workflow with hot reload
|
||||
docker compose --profile development up
|
||||
|
||||
# Production-style container using composed services
|
||||
docker compose --profile production up
|
||||
|
||||
# One-off production container (exposes the app on port 5173)
|
||||
docker run --rm -p 5173:5173 --env-file .env.local bolt-ai:latest
|
||||
```
|
||||
|
||||
When the container starts it runs `pnpm run dockerstart`, which in turn executes `bindings.sh` to pass Cloudflare bindings through Wrangler. You can override this command in `docker-compose.yaml` if you need a different startup routine.
|
||||
|
||||
### Option 3: Desktop Application (Electron)
|
||||
|
||||
For users who prefer a native desktop experience, bolt.diy is also available as an Electron desktop application:
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Message } from 'ai';
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { useAnimate } from 'framer-motion';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useMessageParser, usePromptEnhancer, useShortcuts } from '~/lib/hooks';
|
||||
import { description, useChatHistory } from '~/lib/persistence';
|
||||
import { chatStore } from '~/lib/stores/chat';
|
||||
@@ -29,11 +29,6 @@ import type { TextUIPart, FileUIPart, Attachment } from '@ai-sdk/ui-utils';
|
||||
import { useMCPStore } from '~/lib/stores/mcp';
|
||||
import type { LlmErrorAlertType } from '~/types/actions';
|
||||
|
||||
const toastAnimation = cssTransition({
|
||||
enter: 'animated fadeInRight',
|
||||
exit: 'animated fadeOutRight',
|
||||
});
|
||||
|
||||
const logger = createScopedLogger('Chat');
|
||||
|
||||
export function Chat() {
|
||||
@@ -56,34 +51,6 @@ export function Chat() {
|
||||
importChat={importChat}
|
||||
/>
|
||||
)}
|
||||
<ToastContainer
|
||||
closeButton={({ closeToast }) => {
|
||||
return (
|
||||
<button className="Toastify__close-button" onClick={closeToast}>
|
||||
<div className="i-ph:x text-lg" />
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
icon={({ type }) => {
|
||||
/**
|
||||
* @todo Handle more types if we need them. This may require extra color palettes.
|
||||
*/
|
||||
switch (type) {
|
||||
case 'success': {
|
||||
return <div className="i-ph:check-bold text-bolt-elements-icon-success text-2xl" />;
|
||||
}
|
||||
case 'error': {
|
||||
return <div className="i-ph:warning-circle-bold text-bolt-elements-icon-error text-2xl" />;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}}
|
||||
position="bottom-right"
|
||||
pauseOnFocusLoss
|
||||
transition={toastAnimation}
|
||||
autoClose={3000}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,6 +145,9 @@ export function useGitHubDeploy() {
|
||||
source: 'github',
|
||||
});
|
||||
|
||||
// Show success toast notification
|
||||
toast.success(`🚀 GitHub deployment preparation completed successfully!`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
files: fileContents,
|
||||
|
||||
@@ -145,6 +145,9 @@ export function useGitLabDeploy() {
|
||||
source: 'gitlab',
|
||||
});
|
||||
|
||||
// Show success toast notification
|
||||
toast.success(`🚀 GitLab deployment preparation completed successfully!`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
files: fileContents,
|
||||
|
||||
@@ -224,6 +224,9 @@ export function useNetlifyDeploy() {
|
||||
source: 'netlify',
|
||||
});
|
||||
|
||||
// Show success toast notification
|
||||
toast.success(`🚀 Netlify deployment completed successfully!`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Deploy error:', error);
|
||||
|
||||
@@ -213,6 +213,9 @@ export function useVercelDeploy() {
|
||||
source: 'vercel',
|
||||
});
|
||||
|
||||
// Show success toast notification
|
||||
toast.success(`🚀 Vercel deployment completed successfully!`);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('Vercel deploy error:', err);
|
||||
|
||||
@@ -20,43 +20,47 @@ const PREVIEW_CHANNEL = 'preview-updates';
|
||||
export class PreviewsStore {
|
||||
#availablePreviews = new Map<number, PreviewInfo>();
|
||||
#webcontainer: Promise<WebContainer>;
|
||||
#broadcastChannel: BroadcastChannel;
|
||||
#broadcastChannel?: BroadcastChannel;
|
||||
#lastUpdate = new Map<string, number>();
|
||||
#watchedFiles = new Set<string>();
|
||||
#refreshTimeouts = new Map<string, NodeJS.Timeout>();
|
||||
#REFRESH_DELAY = 300;
|
||||
#storageChannel: BroadcastChannel;
|
||||
#storageChannel?: BroadcastChannel;
|
||||
|
||||
previews = atom<PreviewInfo[]>([]);
|
||||
|
||||
constructor(webcontainerPromise: Promise<WebContainer>) {
|
||||
this.#webcontainer = webcontainerPromise;
|
||||
this.#broadcastChannel = new BroadcastChannel(PREVIEW_CHANNEL);
|
||||
this.#storageChannel = new BroadcastChannel('storage-sync-channel');
|
||||
this.#broadcastChannel = this.#maybeCreateChannel(PREVIEW_CHANNEL);
|
||||
this.#storageChannel = this.#maybeCreateChannel('storage-sync-channel');
|
||||
|
||||
// Listen for preview updates from other tabs
|
||||
this.#broadcastChannel.onmessage = (event) => {
|
||||
const { type, previewId } = event.data;
|
||||
if (this.#broadcastChannel) {
|
||||
// Listen for preview updates from other tabs
|
||||
this.#broadcastChannel.onmessage = (event) => {
|
||||
const { type, previewId } = event.data;
|
||||
|
||||
if (type === 'file-change') {
|
||||
const timestamp = event.data.timestamp;
|
||||
const lastUpdate = this.#lastUpdate.get(previewId) || 0;
|
||||
if (type === 'file-change') {
|
||||
const timestamp = event.data.timestamp;
|
||||
const lastUpdate = this.#lastUpdate.get(previewId) || 0;
|
||||
|
||||
if (timestamp > lastUpdate) {
|
||||
this.#lastUpdate.set(previewId, timestamp);
|
||||
this.refreshPreview(previewId);
|
||||
if (timestamp > lastUpdate) {
|
||||
this.#lastUpdate.set(previewId, timestamp);
|
||||
this.refreshPreview(previewId);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Listen for storage sync messages
|
||||
this.#storageChannel.onmessage = (event) => {
|
||||
const { storage, source } = event.data;
|
||||
if (this.#storageChannel) {
|
||||
// Listen for storage sync messages
|
||||
this.#storageChannel.onmessage = (event) => {
|
||||
const { storage, source } = event.data;
|
||||
|
||||
if (storage && source !== this._getTabId()) {
|
||||
this._syncStorage(storage);
|
||||
}
|
||||
};
|
||||
if (storage && source !== this._getTabId()) {
|
||||
this._syncStorage(storage);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Override localStorage setItem to catch all changes
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -71,6 +75,29 @@ export class PreviewsStore {
|
||||
this.#init();
|
||||
}
|
||||
|
||||
#maybeCreateChannel(name: string): BroadcastChannel | undefined {
|
||||
if (typeof globalThis === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const globalBroadcastChannel = (
|
||||
globalThis as typeof globalThis & {
|
||||
BroadcastChannel?: typeof BroadcastChannel;
|
||||
}
|
||||
).BroadcastChannel;
|
||||
|
||||
if (typeof globalBroadcastChannel !== 'function') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
return new globalBroadcastChannel(name);
|
||||
} catch (error) {
|
||||
console.warn('[Preview] BroadcastChannel unavailable:', error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a unique ID for this tab
|
||||
private _getTabId(): string {
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -130,7 +157,7 @@ export class PreviewsStore {
|
||||
}
|
||||
}
|
||||
|
||||
this.#storageChannel.postMessage({
|
||||
this.#storageChannel?.postMessage({
|
||||
type: 'storage-sync',
|
||||
storage,
|
||||
source: this._getTabId(),
|
||||
@@ -192,7 +219,7 @@ export class PreviewsStore {
|
||||
const timestamp = Date.now();
|
||||
this.#lastUpdate.set(previewId, timestamp);
|
||||
|
||||
this.#broadcastChannel.postMessage({
|
||||
this.#broadcastChannel?.postMessage({
|
||||
type: 'state-change',
|
||||
previewId,
|
||||
timestamp,
|
||||
@@ -204,7 +231,7 @@ export class PreviewsStore {
|
||||
const timestamp = Date.now();
|
||||
this.#lastUpdate.set(previewId, timestamp);
|
||||
|
||||
this.#broadcastChannel.postMessage({
|
||||
this.#broadcastChannel?.postMessage({
|
||||
type: 'file-change',
|
||||
previewId,
|
||||
timestamp,
|
||||
@@ -219,7 +246,7 @@ export class PreviewsStore {
|
||||
const timestamp = Date.now();
|
||||
this.#lastUpdate.set(previewId, timestamp);
|
||||
|
||||
this.#broadcastChannel.postMessage({
|
||||
this.#broadcastChannel?.postMessage({
|
||||
type: 'file-change',
|
||||
previewId,
|
||||
timestamp,
|
||||
|
||||
@@ -52,11 +52,37 @@ export const shortcutsStore = map<Shortcuts>({
|
||||
|
||||
// Create a single key for provider settings
|
||||
const PROVIDER_SETTINGS_KEY = 'provider_settings';
|
||||
const AUTO_ENABLED_KEY = 'auto_enabled_providers';
|
||||
|
||||
// Add this helper function at the top of the file
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
// Initialize provider settings from both localStorage and defaults
|
||||
// Interface for configured provider info from server
|
||||
interface ConfiguredProvider {
|
||||
name: string;
|
||||
isConfigured: boolean;
|
||||
configMethod: 'environment' | 'none';
|
||||
}
|
||||
|
||||
// Fetch configured providers from server
|
||||
const fetchConfiguredProviders = async (): Promise<ConfiguredProvider[]> => {
|
||||
try {
|
||||
const response = await fetch('/api/configured-providers');
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { providers?: ConfiguredProvider[] };
|
||||
|
||||
return data.providers || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching configured providers:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize provider settings from both localStorage and server-detected configuration
|
||||
const getInitialProviderSettings = (): ProviderSetting => {
|
||||
const initialSettings: ProviderSetting = {};
|
||||
|
||||
@@ -92,8 +118,84 @@ const getInitialProviderSettings = (): ProviderSetting => {
|
||||
return initialSettings;
|
||||
};
|
||||
|
||||
// Auto-enable providers that are configured on the server
|
||||
const autoEnableConfiguredProviders = async () => {
|
||||
if (!isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const configuredProviders = await fetchConfiguredProviders();
|
||||
const currentSettings = providersStore.get();
|
||||
const savedSettings = localStorage.getItem(PROVIDER_SETTINGS_KEY);
|
||||
const autoEnabledProviders = localStorage.getItem(AUTO_ENABLED_KEY);
|
||||
|
||||
// Track which providers were auto-enabled to avoid overriding user preferences
|
||||
const previouslyAutoEnabled = autoEnabledProviders ? JSON.parse(autoEnabledProviders) : [];
|
||||
const newlyAutoEnabled: string[] = [];
|
||||
|
||||
let hasChanges = false;
|
||||
|
||||
configuredProviders.forEach(({ name, isConfigured, configMethod }) => {
|
||||
if (isConfigured && configMethod === 'environment' && LOCAL_PROVIDERS.includes(name)) {
|
||||
const currentProvider = currentSettings[name];
|
||||
|
||||
if (currentProvider) {
|
||||
/*
|
||||
* Only auto-enable if:
|
||||
* 1. Provider is not already enabled, AND
|
||||
* 2. Either we haven't saved settings yet (first time) OR provider was previously auto-enabled
|
||||
*/
|
||||
const hasUserSettings = savedSettings !== null;
|
||||
const wasAutoEnabled = previouslyAutoEnabled.includes(name);
|
||||
const shouldAutoEnable = !currentProvider.settings.enabled && (!hasUserSettings || wasAutoEnabled);
|
||||
|
||||
if (shouldAutoEnable) {
|
||||
currentSettings[name] = {
|
||||
...currentProvider,
|
||||
settings: {
|
||||
...currentProvider.settings,
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
newlyAutoEnabled.push(name);
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasChanges) {
|
||||
// Update the store
|
||||
providersStore.set(currentSettings);
|
||||
|
||||
// Save to localStorage
|
||||
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(currentSettings));
|
||||
|
||||
// Update the auto-enabled providers list
|
||||
const allAutoEnabled = [...new Set([...previouslyAutoEnabled, ...newlyAutoEnabled])];
|
||||
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(allAutoEnabled));
|
||||
|
||||
console.log(`Auto-enabled providers: ${newlyAutoEnabled.join(', ')}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error auto-enabling configured providers:', error);
|
||||
}
|
||||
};
|
||||
|
||||
export const providersStore = map<ProviderSetting>(getInitialProviderSettings());
|
||||
|
||||
// Export the auto-enable function for use in components
|
||||
export const initializeProviders = autoEnableConfiguredProviders;
|
||||
|
||||
// Initialize providers when the module loads (in browser only)
|
||||
if (isBrowser) {
|
||||
// Use a small delay to ensure DOM and other resources are ready
|
||||
setTimeout(() => {
|
||||
autoEnableConfiguredProviders();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Create a function to update provider settings that handles both store and persistence
|
||||
export const updateProviderSettings = (provider: string, settings: ProviderSetting) => {
|
||||
const currentSettings = providersStore.get();
|
||||
@@ -113,6 +215,37 @@ export const updateProviderSettings = (provider: string, settings: ProviderSetti
|
||||
// Save to localStorage
|
||||
const allSettings = providersStore.get();
|
||||
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(allSettings));
|
||||
|
||||
// If this is a local provider, update the auto-enabled tracking
|
||||
if (LOCAL_PROVIDERS.includes(provider) && updatedProvider.settings.enabled !== undefined) {
|
||||
updateAutoEnabledTracking(provider, updatedProvider.settings.enabled);
|
||||
}
|
||||
};
|
||||
|
||||
// Update auto-enabled tracking when user manually changes provider settings
|
||||
const updateAutoEnabledTracking = (providerName: string, isEnabled: boolean) => {
|
||||
if (!isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const autoEnabledProviders = localStorage.getItem(AUTO_ENABLED_KEY);
|
||||
const currentAutoEnabled = autoEnabledProviders ? JSON.parse(autoEnabledProviders) : [];
|
||||
|
||||
if (isEnabled) {
|
||||
// If user enables provider, add to auto-enabled list (for future detection)
|
||||
if (!currentAutoEnabled.includes(providerName)) {
|
||||
currentAutoEnabled.push(providerName);
|
||||
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(currentAutoEnabled));
|
||||
}
|
||||
} else {
|
||||
// If user disables provider, remove from auto-enabled list (respect user choice)
|
||||
const updatedAutoEnabled = currentAutoEnabled.filter((name: string) => name !== providerName);
|
||||
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(updatedAutoEnabled));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating auto-enabled tracking:', error);
|
||||
}
|
||||
};
|
||||
|
||||
export const isDebugMode = atom(false);
|
||||
|
||||
@@ -51,8 +51,15 @@ export interface SupabaseConnectionState {
|
||||
credentials?: SupabaseCredentials;
|
||||
}
|
||||
|
||||
const savedConnection = typeof localStorage !== 'undefined' ? localStorage.getItem('supabase_connection') : null;
|
||||
const savedCredentials = typeof localStorage !== 'undefined' ? localStorage.getItem('supabaseCredentials') : null;
|
||||
const storage =
|
||||
typeof globalThis !== 'undefined' &&
|
||||
typeof globalThis.localStorage !== 'undefined' &&
|
||||
typeof globalThis.localStorage.getItem === 'function'
|
||||
? globalThis.localStorage
|
||||
: null;
|
||||
|
||||
const savedConnection = storage ? storage.getItem('supabase_connection') : null;
|
||||
const savedCredentials = storage ? storage.getItem('supabaseCredentials') : null;
|
||||
|
||||
const initialState: SupabaseConnectionState = savedConnection
|
||||
? JSON.parse(savedConnection)
|
||||
@@ -75,14 +82,14 @@ if (savedCredentials && !initialState.credentials) {
|
||||
|
||||
export const supabaseConnection = atom<SupabaseConnectionState>(initialState);
|
||||
|
||||
if (initialState.token && !initialState.stats) {
|
||||
fetchSupabaseStats(initialState.token).catch(console.error);
|
||||
}
|
||||
|
||||
export const isConnecting = atom(false);
|
||||
export const isFetchingStats = atom(false);
|
||||
export const isFetchingApiKeys = atom(false);
|
||||
|
||||
if (initialState.token && !initialState.stats) {
|
||||
fetchSupabaseStats(initialState.token).catch(console.error);
|
||||
}
|
||||
|
||||
export function updateSupabaseConnection(connection: Partial<SupabaseConnectionState>) {
|
||||
const currentState = supabaseConnection.get();
|
||||
|
||||
@@ -123,16 +130,16 @@ export function updateSupabaseConnection(connection: Partial<SupabaseConnectionS
|
||||
* Always save the connection state to localStorage to persist across chats
|
||||
*/
|
||||
if (connection.user || connection.token || connection.selectedProjectId !== undefined || connection.credentials) {
|
||||
localStorage.setItem('supabase_connection', JSON.stringify(newState));
|
||||
storage?.setItem('supabase_connection', JSON.stringify(newState));
|
||||
|
||||
if (newState.credentials) {
|
||||
localStorage.setItem('supabaseCredentials', JSON.stringify(newState.credentials));
|
||||
storage?.setItem('supabaseCredentials', JSON.stringify(newState.credentials));
|
||||
} else {
|
||||
localStorage.removeItem('supabaseCredentials');
|
||||
storage?.removeItem('supabaseCredentials');
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem('supabase_connection');
|
||||
localStorage.removeItem('supabaseCredentials');
|
||||
storage?.removeItem('supabase_connection');
|
||||
storage?.removeItem('supabaseCredentials');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
app/root.tsx
31
app/root.tsx
@@ -9,6 +9,7 @@ import { useEffect } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { ClientOnly } from 'remix-utils/client-only';
|
||||
import { cssTransition, ToastContainer } from 'react-toastify';
|
||||
|
||||
import reactToastifyStyles from 'react-toastify/dist/ReactToastify.css?url';
|
||||
import globalStyles from './styles/index.scss?url';
|
||||
@@ -16,6 +17,11 @@ import xtermStyles from '@xterm/xterm/css/xterm.css?url';
|
||||
|
||||
import 'virtual:uno.css';
|
||||
|
||||
const toastAnimation = cssTransition({
|
||||
enter: 'animated fadeInRight',
|
||||
exit: 'animated fadeOutRight',
|
||||
});
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
{
|
||||
rel: 'icon',
|
||||
@@ -75,6 +81,31 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<ClientOnly>{() => <DndProvider backend={HTML5Backend}>{children}</DndProvider>}</ClientOnly>
|
||||
<ToastContainer
|
||||
closeButton={({ closeToast }) => {
|
||||
return (
|
||||
<button className="Toastify__close-button" onClick={closeToast}>
|
||||
<div className="i-ph:x text-lg" />
|
||||
</button>
|
||||
);
|
||||
}}
|
||||
icon={({ type }) => {
|
||||
switch (type) {
|
||||
case 'success': {
|
||||
return <div className="i-ph:check-bold text-bolt-elements-icon-success text-2xl" />;
|
||||
}
|
||||
case 'error': {
|
||||
return <div className="i-ph:warning-circle-bold text-bolt-elements-icon-error text-2xl" />;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}}
|
||||
position="bottom-right"
|
||||
pauseOnFocusLoss
|
||||
transition={toastAnimation}
|
||||
autoClose={3000}
|
||||
/>
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
</>
|
||||
|
||||
110
app/routes/api.configured-providers.ts
Normal file
110
app/routes/api.configured-providers.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import type { LoaderFunction } from '@remix-run/cloudflare';
|
||||
import { json } from '@remix-run/cloudflare';
|
||||
import { LLMManager } from '~/lib/modules/llm/manager';
|
||||
import { LOCAL_PROVIDERS } from '~/lib/stores/settings';
|
||||
|
||||
interface ConfiguredProvider {
|
||||
name: string;
|
||||
isConfigured: boolean;
|
||||
configMethod: 'environment' | 'none';
|
||||
}
|
||||
|
||||
interface ConfiguredProvidersResponse {
|
||||
providers: ConfiguredProvider[];
|
||||
}
|
||||
|
||||
/**
|
||||
* API endpoint that detects which providers are configured via environment variables
|
||||
* This helps auto-enable providers that have been set up by the user
|
||||
*/
|
||||
export const loader: LoaderFunction = async ({ context }) => {
|
||||
try {
|
||||
const llmManager = LLMManager.getInstance(context?.cloudflare?.env as any);
|
||||
const configuredProviders: ConfiguredProvider[] = [];
|
||||
|
||||
// Check each local provider for environment configuration
|
||||
for (const providerName of LOCAL_PROVIDERS) {
|
||||
const providerInstance = llmManager.getProvider(providerName);
|
||||
let isConfigured = false;
|
||||
let configMethod: 'environment' | 'none' = 'none';
|
||||
|
||||
if (providerInstance) {
|
||||
const config = providerInstance.config;
|
||||
|
||||
/*
|
||||
* Check if required environment variables are set
|
||||
* For providers with baseUrlKey (Ollama, LMStudio, OpenAILike)
|
||||
*/
|
||||
if (config.baseUrlKey) {
|
||||
const baseUrlEnvVar = config.baseUrlKey;
|
||||
const cloudflareEnv = (context?.cloudflare?.env as Record<string, any>)?.[baseUrlEnvVar];
|
||||
const processEnv = process.env[baseUrlEnvVar];
|
||||
const managerEnv = llmManager.env[baseUrlEnvVar];
|
||||
|
||||
const envBaseUrl = cloudflareEnv || processEnv || managerEnv;
|
||||
|
||||
/*
|
||||
* Only consider configured if environment variable is explicitly set
|
||||
* Don't count default config.baseUrl values or placeholder values
|
||||
*/
|
||||
const isValidEnvValue =
|
||||
envBaseUrl &&
|
||||
typeof envBaseUrl === 'string' &&
|
||||
envBaseUrl.trim().length > 0 &&
|
||||
!envBaseUrl.includes('your_') && // Filter out placeholder values like "your_openai_like_base_url_here"
|
||||
!envBaseUrl.includes('_here') &&
|
||||
envBaseUrl.startsWith('http'); // Must be a valid URL
|
||||
|
||||
if (isValidEnvValue) {
|
||||
isConfigured = true;
|
||||
configMethod = 'environment';
|
||||
}
|
||||
}
|
||||
|
||||
// For providers that might need API keys as well (check this separately, not as fallback)
|
||||
if (config.apiTokenKey && !isConfigured) {
|
||||
const apiTokenEnvVar = config.apiTokenKey;
|
||||
const envApiToken =
|
||||
(context?.cloudflare?.env as Record<string, any>)?.[apiTokenEnvVar] ||
|
||||
process.env[apiTokenEnvVar] ||
|
||||
llmManager.env[apiTokenEnvVar];
|
||||
|
||||
// Only consider configured if API key is set and not a placeholder
|
||||
const isValidApiToken =
|
||||
envApiToken &&
|
||||
typeof envApiToken === 'string' &&
|
||||
envApiToken.trim().length > 0 &&
|
||||
!envApiToken.includes('your_') && // Filter out placeholder values
|
||||
!envApiToken.includes('_here') &&
|
||||
envApiToken.length > 10; // API keys are typically longer than 10 chars
|
||||
|
||||
if (isValidApiToken) {
|
||||
isConfigured = true;
|
||||
configMethod = 'environment';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuredProviders.push({
|
||||
name: providerName,
|
||||
isConfigured,
|
||||
configMethod,
|
||||
});
|
||||
}
|
||||
|
||||
return json<ConfiguredProvidersResponse>({
|
||||
providers: configuredProviders,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error detecting configured providers:', error);
|
||||
|
||||
// Return default state on error
|
||||
return json<ConfiguredProvidersResponse>({
|
||||
providers: LOCAL_PROVIDERS.map((name) => ({
|
||||
name,
|
||||
isConfigured: false,
|
||||
configMethod: 'none' as const,
|
||||
})),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -46,17 +46,22 @@ export default function WebContainerPreview() {
|
||||
}, [previewId, previewUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize broadcast channel
|
||||
broadcastChannelRef.current = new BroadcastChannel(PREVIEW_CHANNEL);
|
||||
const supportsBroadcastChannel = typeof window !== 'undefined' && typeof window.BroadcastChannel === 'function';
|
||||
|
||||
// Listen for preview updates
|
||||
broadcastChannelRef.current.onmessage = (event) => {
|
||||
if (event.data.previewId === previewId) {
|
||||
if (event.data.type === 'refresh-preview' || event.data.type === 'file-change') {
|
||||
handleRefresh();
|
||||
if (supportsBroadcastChannel) {
|
||||
broadcastChannelRef.current = new window.BroadcastChannel(PREVIEW_CHANNEL);
|
||||
|
||||
// Listen for preview updates
|
||||
broadcastChannelRef.current.onmessage = (event) => {
|
||||
if (event.data.previewId === previewId) {
|
||||
if (event.data.type === 'refresh-preview' || event.data.type === 'file-change') {
|
||||
handleRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
broadcastChannelRef.current = undefined;
|
||||
}
|
||||
|
||||
// Construct the WebContainer preview URL
|
||||
const url = `https://${previewId}.local-credentialless.webcontainer-api.io`;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
@use '../z-index';
|
||||
|
||||
.Toastify__toast-container {
|
||||
@extend .z-toast;
|
||||
}
|
||||
|
||||
.Toastify__toast {
|
||||
--at-apply: shadow-md;
|
||||
|
||||
|
||||
@@ -31,3 +31,7 @@ $zIndexMax: 999;
|
||||
.z-max {
|
||||
z-index: $zIndexMax;
|
||||
}
|
||||
|
||||
.z-toast {
|
||||
z-index: $zIndexMax + 1;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ services:
|
||||
app-dev:
|
||||
image: bolt-ai:development
|
||||
build:
|
||||
target: bolt-ai-development
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: development
|
||||
env_file:
|
||||
- '.env'
|
||||
- '.env.local'
|
||||
|
||||
34848
package-lock.json
generated
Normal file
34848
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@
|
||||
"dockerstart": "bindings=$(./bindings.sh) && wrangler pages dev ./build/client $bindings --ip 0.0.0.0 --port 5173 --no-show-interactive-dev-session",
|
||||
"dockerrun": "docker run -it -d --name bolt-ai-live -p 5173:5173 --env-file .env.local bolt-ai",
|
||||
"dockerbuild:prod": "docker build -t bolt-ai:production -t bolt-ai:latest --target bolt-ai-production .",
|
||||
"dockerbuild": "docker build -t bolt-ai:development -t bolt-ai:latest --target bolt-ai-development .",
|
||||
"dockerbuild": "docker build -t bolt-ai:development -t bolt-ai:latest --target development .",
|
||||
"typecheck": "tsc",
|
||||
"typegen": "wrangler types",
|
||||
"preview": "pnpm run build && pnpm run start",
|
||||
@@ -161,7 +161,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitz/eslint-plugin": "0.1.0",
|
||||
"@cloudflare/workers-types": "^4.20241127.0",
|
||||
"@cloudflare/workers-types": "^4.20251011.0",
|
||||
"@electron/notarize": "^2.5.0",
|
||||
"@iconify-json/ph": "^1.2.1",
|
||||
"@iconify/types": "^2.0.0",
|
||||
@@ -204,7 +204,7 @@
|
||||
"vite-plugin-optimize-css-modules": "^1.1.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^2.1.7",
|
||||
"wrangler": "^4.5.1"
|
||||
"wrangler": "^4.44.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@typescript-eslint/utils": "^8.0.0-alpha.30"
|
||||
|
||||
152
pnpm-lock.yaml
generated
152
pnpm-lock.yaml
generated
@@ -157,10 +157,10 @@ importers:
|
||||
version: 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@remix-run/cloudflare':
|
||||
specifier: ^2.15.2
|
||||
version: 2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)
|
||||
version: 2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)
|
||||
'@remix-run/cloudflare-pages':
|
||||
specifier: ^2.15.2
|
||||
version: 2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)
|
||||
version: 2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)
|
||||
'@remix-run/node':
|
||||
specifier: ^2.15.2
|
||||
version: 2.16.8(typescript@5.8.3)
|
||||
@@ -322,7 +322,7 @@ importers:
|
||||
version: 0.2.0(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/server-runtime@2.16.8(typescript@5.8.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
remix-utils:
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3))(@remix-run/node@2.16.8(typescript@5.8.3))(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/router@1.23.0)(react@18.3.1)(zod@3.25.76)
|
||||
version: 7.7.0(@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3))(@remix-run/node@2.16.8(typescript@5.8.3))(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/router@1.23.0)(react@18.3.1)(zod@3.25.76)
|
||||
rollup-plugin-node-polyfills:
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1
|
||||
@@ -352,8 +352,8 @@ importers:
|
||||
specifier: 0.1.0
|
||||
version: 0.1.0(jiti@1.21.7)(prettier@3.6.2)(typescript@5.8.3)
|
||||
'@cloudflare/workers-types':
|
||||
specifier: ^4.20241127.0
|
||||
version: 4.20250722.0
|
||||
specifier: ^4.20251011.0
|
||||
version: 4.20251014.0
|
||||
'@electron/notarize':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
@@ -365,7 +365,7 @@ importers:
|
||||
version: 2.0.0
|
||||
'@remix-run/dev':
|
||||
specifier: ^2.15.2
|
||||
version: 2.16.8(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.8(typescript@5.8.3))(@types/node@24.1.0)(sass-embedded@1.89.2)(typescript@5.8.3)(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2))(wrangler@4.25.1(@cloudflare/workers-types@4.20250722.0))
|
||||
version: 2.16.8(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.8(typescript@5.8.3))(@types/node@24.1.0)(sass-embedded@1.89.2)(typescript@5.8.3)(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2))(wrangler@4.44.0(@cloudflare/workers-types@4.20251014.0))
|
||||
'@remix-run/serve':
|
||||
specifier: ^2.15.2
|
||||
version: 2.16.8(typescript@5.8.3)
|
||||
@@ -419,7 +419,7 @@ importers:
|
||||
version: 33.4.11
|
||||
electron-builder:
|
||||
specifier: ^26.0.12
|
||||
version: 26.0.12(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12))
|
||||
version: 26.0.12(electron-builder-squirrel-windows@26.0.12)
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.1
|
||||
version: 10.1.8(eslint@9.31.0(jiti@1.21.7))
|
||||
@@ -481,8 +481,8 @@ importers:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.9(@types/node@24.1.0)(jsdom@26.1.0)(sass-embedded@1.89.2)
|
||||
wrangler:
|
||||
specifier: ^4.5.1
|
||||
version: 4.25.1(@cloudflare/workers-types@4.20250722.0)
|
||||
specifier: ^4.44.0
|
||||
version: 4.44.0(@cloudflare/workers-types@4.20251014.0)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -954,47 +954,47 @@ packages:
|
||||
resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@cloudflare/unenv-preset@2.3.3':
|
||||
resolution: {integrity: sha512-/M3MEcj3V2WHIRSW1eAQBPRJ6JnGQHc6JKMAPLkDb7pLs3m6X9ES/+K3ceGqxI6TKeF32AWAi7ls0AYzVxCP0A==}
|
||||
'@cloudflare/unenv-preset@2.7.8':
|
||||
resolution: {integrity: sha512-Ky929MfHh+qPhwCapYrRPwPVHtA2Ioex/DbGZyskGyNRDe9Ru3WThYZivyNVaPy5ergQSgMs9OKrM9Ajtz9F6w==}
|
||||
peerDependencies:
|
||||
unenv: 2.0.0-rc.17
|
||||
workerd: ^1.20250508.0
|
||||
unenv: 2.0.0-rc.21
|
||||
workerd: ^1.20250927.0
|
||||
peerDependenciesMeta:
|
||||
workerd:
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workerd-darwin-64@1.20250712.0':
|
||||
resolution: {integrity: sha512-M6S6a/LQ0Jb0R+g0XhlYi1adGifvYmxA5mD/i9TuZZgjs2bIm5ELuka/n3SCnI98ltvlx3HahRaHagAtOilsFg==}
|
||||
'@cloudflare/workerd-darwin-64@1.20251011.0':
|
||||
resolution: {integrity: sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg==}
|
||||
engines: {node: '>=16'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@cloudflare/workerd-darwin-arm64@1.20250712.0':
|
||||
resolution: {integrity: sha512-7sFzn6rvAcnLy7MktFL42dYtzL0Idw/kiUmNf2P3TvsBRoShhLK5ZKhbw+NAhvU8e4pXWm5lkE0XmpieA0zNjw==}
|
||||
'@cloudflare/workerd-darwin-arm64@1.20251011.0':
|
||||
resolution: {integrity: sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA==}
|
||||
engines: {node: '>=16'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@cloudflare/workerd-linux-64@1.20250712.0':
|
||||
resolution: {integrity: sha512-EFRrGe/bqK7NHtht7vNlbrDpfvH3eRvtJOgsTpEQEysDjVmlK6pVJxSnLy9Hg1zlLY15IfhfGC+K2qisseHGJQ==}
|
||||
'@cloudflare/workerd-linux-64@1.20251011.0':
|
||||
resolution: {integrity: sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA==}
|
||||
engines: {node: '>=16'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@cloudflare/workerd-linux-arm64@1.20250712.0':
|
||||
resolution: {integrity: sha512-rG8JUleddhUHQVwpXOYv0VbL0S9kOtR9PNKecgVhFpxEhC8aTeg2HNBBjo8st7IfcUvY8WaW3pD3qdAMZ05UwQ==}
|
||||
'@cloudflare/workerd-linux-arm64@1.20251011.0':
|
||||
resolution: {integrity: sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ==}
|
||||
engines: {node: '>=16'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@cloudflare/workerd-windows-64@1.20250712.0':
|
||||
resolution: {integrity: sha512-qS8H5RCYwE21Om9wo5/F807ClBJIfknhuLBj16eYxvJcj9JqgAKWi12BGgjyGxHuJJjeoQ63lr4wHAdbFntDDg==}
|
||||
'@cloudflare/workerd-windows-64@1.20251011.0':
|
||||
resolution: {integrity: sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg==}
|
||||
engines: {node: '>=16'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@cloudflare/workers-types@4.20250722.0':
|
||||
resolution: {integrity: sha512-pTY+A07DTSacgUBYcVEEb78/KG7THdcRpPqXLeH/A/LHHobAddgN4zyXBldsoZuzy7bD9tZYJW+wkcyR4k7fDA==}
|
||||
'@cloudflare/workers-types@4.20251014.0':
|
||||
resolution: {integrity: sha512-tEW98J/kOa0TdylIUOrLKRdwkUw0rvvYVlo+Ce0mqRH3c8kSoxLzUH9gfCvwLe0M89z1RkzFovSKAW2Nwtyn3w==}
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==}
|
||||
@@ -6068,8 +6068,8 @@ packages:
|
||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
miniflare@4.20250712.1:
|
||||
resolution: {integrity: sha512-46gB3FGPOsy+EpFGufjhr8agYycO/55d6l0y7hNJ13NcTVwrObMg/0HmI3pC5yQj0974IVXzBgUfDBMAX6thow==}
|
||||
miniflare@4.20251011.0:
|
||||
resolution: {integrity: sha512-DlZ7vR5q/RE9eLsxsrXzfSZIF2f6O5k0YsFrSKhWUtdefyGtJt4sSpR6V+Af/waaZ6+zIFy9lsknHBCm49sEYA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -7765,12 +7765,12 @@ packages:
|
||||
resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
undici@7.12.0:
|
||||
resolution: {integrity: sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==}
|
||||
undici@7.14.0:
|
||||
resolution: {integrity: sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unenv@2.0.0-rc.17:
|
||||
resolution: {integrity: sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg==}
|
||||
unenv@2.0.0-rc.21:
|
||||
resolution: {integrity: sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==}
|
||||
|
||||
unified@10.1.2:
|
||||
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
|
||||
@@ -8137,17 +8137,17 @@ packages:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
workerd@1.20250712.0:
|
||||
resolution: {integrity: sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg==}
|
||||
workerd@1.20251011.0:
|
||||
resolution: {integrity: sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
wrangler@4.25.1:
|
||||
resolution: {integrity: sha512-4Tlg+jmqxCX3xFm+Nz1b4jHHY9iOu1EyJ17SSCCJ6MGp+FCGtXgr+CynT94+MP0v/qKQUkMKjoeJ5FNDunZ9cA==}
|
||||
wrangler@4.44.0:
|
||||
resolution: {integrity: sha512-BLOUigckcWZ0r4rm7b5PuaTpb9KP9as0XeCRSJ8kqcNgXcKoUD3Ij8FlPvN25KybLnFnetaO0ZdfRYUPWle4qw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@cloudflare/workers-types': ^4.20250712.0
|
||||
'@cloudflare/workers-types': ^4.20251011.0
|
||||
peerDependenciesMeta:
|
||||
'@cloudflare/workers-types':
|
||||
optional: true
|
||||
@@ -9104,28 +9104,28 @@ snapshots:
|
||||
dependencies:
|
||||
mime: 3.0.0
|
||||
|
||||
'@cloudflare/unenv-preset@2.3.3(unenv@2.0.0-rc.17)(workerd@1.20250712.0)':
|
||||
'@cloudflare/unenv-preset@2.7.8(unenv@2.0.0-rc.21)(workerd@1.20251011.0)':
|
||||
dependencies:
|
||||
unenv: 2.0.0-rc.17
|
||||
unenv: 2.0.0-rc.21
|
||||
optionalDependencies:
|
||||
workerd: 1.20250712.0
|
||||
workerd: 1.20251011.0
|
||||
|
||||
'@cloudflare/workerd-darwin-64@1.20250712.0':
|
||||
'@cloudflare/workerd-darwin-64@1.20251011.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workerd-darwin-arm64@1.20250712.0':
|
||||
'@cloudflare/workerd-darwin-arm64@1.20251011.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workerd-linux-64@1.20250712.0':
|
||||
'@cloudflare/workerd-linux-64@1.20251011.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workerd-linux-arm64@1.20250712.0':
|
||||
'@cloudflare/workerd-linux-arm64@1.20251011.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workerd-windows-64@1.20250712.0':
|
||||
'@cloudflare/workerd-windows-64@1.20251011.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workers-types@4.20250722.0': {}
|
||||
'@cloudflare/workers-types@4.20251014.0': {}
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
dependencies:
|
||||
@@ -10765,22 +10765,22 @@ snapshots:
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
'@remix-run/cloudflare-pages@2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)':
|
||||
'@remix-run/cloudflare-pages@2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@cloudflare/workers-types': 4.20250722.0
|
||||
'@remix-run/cloudflare': 2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)
|
||||
'@cloudflare/workers-types': 4.20251014.0
|
||||
'@remix-run/cloudflare': 2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
'@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)':
|
||||
'@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@cloudflare/kv-asset-handler': 0.1.3
|
||||
'@cloudflare/workers-types': 4.20250722.0
|
||||
'@cloudflare/workers-types': 4.20251014.0
|
||||
'@remix-run/server-runtime': 2.16.8(typescript@5.8.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
'@remix-run/dev@2.16.8(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.8(typescript@5.8.3))(@types/node@24.1.0)(sass-embedded@1.89.2)(typescript@5.8.3)(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2))(wrangler@4.25.1(@cloudflare/workers-types@4.20250722.0))':
|
||||
'@remix-run/dev@2.16.8(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.8(typescript@5.8.3))(@types/node@24.1.0)(sass-embedded@1.89.2)(typescript@5.8.3)(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2))(wrangler@4.44.0(@cloudflare/workers-types@4.20251014.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.0
|
||||
'@babel/generator': 7.28.0
|
||||
@@ -10843,7 +10843,7 @@ snapshots:
|
||||
'@remix-run/serve': 2.16.8(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
vite: 5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)
|
||||
wrangler: 4.25.1(@cloudflare/workers-types@4.20250722.0)
|
||||
wrangler: 4.44.0(@cloudflare/workers-types@4.20251014.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
@@ -12074,7 +12074,7 @@ snapshots:
|
||||
|
||||
app-builder-bin@5.0.0-alpha.12: {}
|
||||
|
||||
app-builder-lib@26.0.12(dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12))(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12)):
|
||||
app-builder-lib@26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12):
|
||||
dependencies:
|
||||
'@develar/schema-utils': 2.6.5
|
||||
'@electron/asar': 3.2.18
|
||||
@@ -12887,7 +12887,7 @@ snapshots:
|
||||
|
||||
dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12):
|
||||
dependencies:
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12))(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12))
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12)
|
||||
builder-util: 26.0.11
|
||||
builder-util-runtime: 9.3.1
|
||||
fs-extra: 10.1.0
|
||||
@@ -12966,7 +12966,7 @@ snapshots:
|
||||
|
||||
electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12):
|
||||
dependencies:
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12))(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12))
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12)
|
||||
builder-util: 26.0.11
|
||||
electron-winstaller: 5.4.0
|
||||
transitivePeerDependencies:
|
||||
@@ -12974,9 +12974,9 @@ snapshots:
|
||||
- dmg-builder
|
||||
- supports-color
|
||||
|
||||
electron-builder@26.0.12(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12)):
|
||||
electron-builder@26.0.12(electron-builder-squirrel-windows@26.0.12):
|
||||
dependencies:
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12))(electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12))
|
||||
app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12)
|
||||
builder-util: 26.0.11
|
||||
builder-util-runtime: 9.3.1
|
||||
chalk: 4.1.2
|
||||
@@ -15269,7 +15269,7 @@ snapshots:
|
||||
|
||||
min-indent@1.0.1: {}
|
||||
|
||||
miniflare@4.20250712.1:
|
||||
miniflare@4.20251011.0:
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
acorn: 8.14.0
|
||||
@@ -15278,8 +15278,8 @@ snapshots:
|
||||
glob-to-regexp: 0.4.1
|
||||
sharp: 0.33.5
|
||||
stoppable: 1.1.0
|
||||
undici: 7.12.0
|
||||
workerd: 1.20250712.0
|
||||
undici: 7.14.0
|
||||
workerd: 1.20251011.0
|
||||
ws: 8.18.0
|
||||
youch: 4.1.0-beta.10
|
||||
zod: 3.22.3
|
||||
@@ -16229,11 +16229,11 @@ snapshots:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
remix-utils@7.7.0(@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3))(@remix-run/node@2.16.8(typescript@5.8.3))(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/router@1.23.0)(react@18.3.1)(zod@3.25.76):
|
||||
remix-utils@7.7.0(@remix-run/cloudflare@2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3))(@remix-run/node@2.16.8(typescript@5.8.3))(@remix-run/react@2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/router@1.23.0)(react@18.3.1)(zod@3.25.76):
|
||||
dependencies:
|
||||
type-fest: 4.41.0
|
||||
optionalDependencies:
|
||||
'@remix-run/cloudflare': 2.16.8(@cloudflare/workers-types@4.20250722.0)(typescript@5.8.3)
|
||||
'@remix-run/cloudflare': 2.16.8(@cloudflare/workers-types@4.20251014.0)(typescript@5.8.3)
|
||||
'@remix-run/node': 2.16.8(typescript@5.8.3)
|
||||
'@remix-run/react': 2.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
|
||||
'@remix-run/router': 1.23.0
|
||||
@@ -17072,9 +17072,9 @@ snapshots:
|
||||
|
||||
undici@6.21.3: {}
|
||||
|
||||
undici@7.12.0: {}
|
||||
undici@7.14.0: {}
|
||||
|
||||
unenv@2.0.0-rc.17:
|
||||
unenv@2.0.0-rc.21:
|
||||
dependencies:
|
||||
defu: 6.1.4
|
||||
exsolve: 1.0.7
|
||||
@@ -17522,26 +17522,26 @@ snapshots:
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
workerd@1.20250712.0:
|
||||
workerd@1.20251011.0:
|
||||
optionalDependencies:
|
||||
'@cloudflare/workerd-darwin-64': 1.20250712.0
|
||||
'@cloudflare/workerd-darwin-arm64': 1.20250712.0
|
||||
'@cloudflare/workerd-linux-64': 1.20250712.0
|
||||
'@cloudflare/workerd-linux-arm64': 1.20250712.0
|
||||
'@cloudflare/workerd-windows-64': 1.20250712.0
|
||||
'@cloudflare/workerd-darwin-64': 1.20251011.0
|
||||
'@cloudflare/workerd-darwin-arm64': 1.20251011.0
|
||||
'@cloudflare/workerd-linux-64': 1.20251011.0
|
||||
'@cloudflare/workerd-linux-arm64': 1.20251011.0
|
||||
'@cloudflare/workerd-windows-64': 1.20251011.0
|
||||
|
||||
wrangler@4.25.1(@cloudflare/workers-types@4.20250722.0):
|
||||
wrangler@4.44.0(@cloudflare/workers-types@4.20251014.0):
|
||||
dependencies:
|
||||
'@cloudflare/kv-asset-handler': 0.4.0
|
||||
'@cloudflare/unenv-preset': 2.3.3(unenv@2.0.0-rc.17)(workerd@1.20250712.0)
|
||||
'@cloudflare/unenv-preset': 2.7.8(unenv@2.0.0-rc.21)(workerd@1.20251011.0)
|
||||
blake3-wasm: 2.1.5
|
||||
esbuild: 0.25.4
|
||||
miniflare: 4.20250712.1
|
||||
miniflare: 4.20251011.0
|
||||
path-to-regexp: 6.3.0
|
||||
unenv: 2.0.0-rc.17
|
||||
workerd: 1.20250712.0
|
||||
unenv: 2.0.0-rc.21
|
||||
workerd: 1.20251011.0
|
||||
optionalDependencies:
|
||||
'@cloudflare/workers-types': 4.20250722.0
|
||||
'@cloudflare/workers-types': 4.20251014.0
|
||||
fsevents: 2.3.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
||||
Reference in New Issue
Block a user