├── public ├── robots.txt ├── manifest.json └── favicon.svg ├── .nvmrc ├── src ├── components │ ├── footer │ │ ├── footer.module.css │ │ └── footer.tsx │ ├── header │ │ ├── header.module.css │ │ └── header.tsx │ ├── card │ │ └── card.tsx │ ├── router-head │ │ └── router-head.tsx │ ├── browser-state │ │ └── browser-state.tsx │ ├── loading │ │ └── loading.tsx │ ├── question │ │ └── question.tsx │ ├── prompt │ │ └── prompt.tsx │ ├── actions │ │ └── actions.tsx │ └── render-result │ │ └── render-result.tsx ├── constants │ └── prisma-client.ts ├── routes │ ├── layout.tsx │ ├── api │ │ └── v1 │ │ │ ├── launch │ │ │ └── index.tsx │ │ │ ├── run │ │ │ └── index.ts │ │ │ └── ai │ │ │ └── index.ts │ ├── service-worker.ts │ └── index.tsx ├── functions │ ├── get-browser-state.ts │ ├── questions.ts │ ├── remove-nth-query-params.ts │ ├── stream-completion.tsx │ └── run-action.ts ├── global.css ├── entry.dev.tsx ├── entry.preview.tsx ├── plugins │ ├── index.ts │ ├── exec.ts │ ├── fs.ts │ └── github.ts ├── entry.ssr.tsx ├── root.tsx └── prompts │ └── get-action.ts ├── .prettierrc.json ├── postcss.config.js ├── .vscode └── extensions.json ├── tailwind.config.js ├── .env.template ├── docker-compose.yml ├── Dockerfile ├── docker-compose.debug.yml ├── .dockerignore ├── vite.config.ts ├── .eslintignore ├── .prettierignore ├── .gitignore ├── tsconfig.json ├── prisma └── schema.prisma ├── .eslintrc.cjs ├── package.json └── README.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.14.0 2 | -------------------------------------------------------------------------------- /src/components/footer/footer.module.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /src/components/header/header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | } 3 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "unifiedjs.vscode-mdx"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/components/footer/footer.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | 3 | export default component$(() => { 4 | return ; 5 | }); 6 | -------------------------------------------------------------------------------- /src/constants/prisma-client.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | import { isServer } from '@builder.io/qwik/build'; 3 | 4 | export const prismaClient = isServer ? new PrismaClient() : null; 5 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/components/header/header.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | import styles from './header.module.css'; 3 | 4 | export default component$(() => { 5 | return
; 6 | }); 7 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | OPENAI_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 2 | MODEL=gpt-4 3 | 4 | # Postgres DB URL 5 | DATABASE_URL="postgres://postgres:xxx@xxx:5432/postgres?schema=public" 6 | 7 | # Optional - for github plugin support 8 | GITHUB_USERNAME=xxx 9 | GITHUB_PASSWORD=xxx -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | builderioaiagent: 5 | image: builderioaiagent 6 | build: 7 | context: . 8 | dockerfile: ./Dockerfile 9 | environment: 10 | NODE_ENV: production 11 | ports: 12 | - 5173:5173 13 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/web-manifest-combined.json", 3 | "name": "qwik-project-name", 4 | "short_name": "Welcome to Qwik", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#fff", 8 | "description": "A Qwik project app." 9 | } 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | ENV NODE_ENV=production 3 | WORKDIR /usr/src/app 4 | COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] 5 | RUN npm install --production --silent && mv node_modules ../ 6 | COPY . . 7 | EXPOSE 5173 8 | RUN chown -R node /usr/src/app 9 | USER node 10 | CMD ["npm", "start"] 11 | -------------------------------------------------------------------------------- /src/components/card/card.tsx: -------------------------------------------------------------------------------- 1 | import { Slot, component$ } from '@builder.io/qwik'; 2 | 3 | export const Card = component$((props: { class?: string }) => { 4 | return ( 5 |
10 | 11 |
12 | ); 13 | }); 14 | -------------------------------------------------------------------------------- /docker-compose.debug.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | builderioaiagent: 5 | image: builderioaiagent 6 | build: 7 | context: . 8 | dockerfile: ./Dockerfile 9 | environment: 10 | NODE_ENV: development 11 | ports: 12 | - 5173:5173 13 | - 9229:9229 14 | command: ["node", "--inspect=0.0.0.0:9229", "index.js"] 15 | -------------------------------------------------------------------------------- /src/routes/layout.tsx: -------------------------------------------------------------------------------- 1 | import { component$, Slot } from '@builder.io/qwik'; 2 | 3 | import Header from '~/components/header/header'; 4 | import Footer from '~/components/footer/footer'; 5 | 6 | export default component$(() => { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /src/functions/get-browser-state.ts: -------------------------------------------------------------------------------- 1 | import { server$ } from '@builder.io/qwik-city'; 2 | import type { BrowserState } from '@prisma/client'; 3 | import { prismaClient } from '~/constants/prisma-client'; 4 | 5 | export const getBrowserState = server$( 6 | async (): Promise => { 7 | const browserState = await prismaClient!.browserState.findFirst(); 8 | return browserState; 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/charts 15 | **/docker-compose* 16 | **/compose* 17 | **/Dockerfile* 18 | **/node_modules 19 | **/npm-debug.log 20 | **/obj 21 | **/secrets.dev.yaml 22 | **/values.dev.yaml 23 | LICENSE 24 | README.md 25 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { qwikVite } from '@builder.io/qwik/optimizer'; 3 | import { qwikCity } from '@builder.io/qwik-city/vite'; 4 | import tsconfigPaths from 'vite-tsconfig-paths'; 5 | 6 | export default defineConfig(() => { 7 | return { 8 | plugins: [qwikCity(), qwikVite(), tsconfigPaths()], 9 | preview: { 10 | headers: { 11 | 'Cache-Control': 'public, max-age=600', 12 | }, 13 | }, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Tailwind CSS imports 3 | * View the full documentation at https://tailwindcss.com 4 | */ 5 | @tailwind base; 6 | @tailwind components; 7 | @tailwind utilities; 8 | 9 | /** 10 | * WHAT IS THIS FILE? 11 | * 12 | * Globally applied styles. No matter which components are in the page or matching route, 13 | * the styles in here will be applied to the Document, without any sort of CSS scoping. 14 | * 15 | */ 16 | 17 | :root { 18 | @apply bg-gray-100; 19 | } 20 | -------------------------------------------------------------------------------- /src/routes/api/v1/launch/index.tsx: -------------------------------------------------------------------------------- 1 | import type { RequestEvent } from '@builder.io/qwik-city'; 2 | import { z } from '@builder.io/qwik-city'; 3 | 4 | const schema = z.object({ 5 | prompt: z.string(), 6 | }); 7 | 8 | export async function onPost(request: RequestEvent) { 9 | const body = await request.parseBody(); 10 | const value = schema.parse(body); 11 | return request.json(200, { 12 | redirect: `http://localhost:5173/?run=${encodeURIComponent(value.prompt)}`, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | *.spec.tsx 33 | *.spec.ts 34 | .netlify 35 | pnpm-lock.yaml 36 | package-lock.json 37 | yarn.lock 38 | server 39 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | *.spec.tsx 33 | *.spec.ts 34 | .netlify 35 | pnpm-lock.yaml 36 | package-lock.json 37 | yarn.lock 38 | server 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | /working-dir 7 | 8 | # Development 9 | node_modules 10 | 11 | # Cache 12 | .cache 13 | .mf 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | .vscode/* 28 | !.vscode/extensions.json 29 | .idea 30 | .DS_Store 31 | *.suo 32 | *.ntvs* 33 | *.njsproj 34 | *.sln 35 | *.sw? 36 | 37 | # Yarn 38 | .yarn/* 39 | !.yarn/releases 40 | 41 | 42 | .env 43 | 44 | .cookies.json -------------------------------------------------------------------------------- /src/entry.dev.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * Development entry point using only client-side modules: 5 | * - Do not use this mode in production! 6 | * - No SSR 7 | * - No portion of the application is pre-rendered on the server. 8 | * - All of the application is running eagerly in the browser. 9 | * - More code is transferred to the browser than in SSR mode. 10 | * - Optimizer/Serialization/Deserialization code is not exercised! 11 | */ 12 | import { render, type RenderOptions } from '@builder.io/qwik'; 13 | import Root from './root'; 14 | 15 | export default function (opts: RenderOptions) { 16 | return render(document, , opts); 17 | } 18 | -------------------------------------------------------------------------------- /src/entry.preview.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * It's the bundle entry point for `npm run preview`. 5 | * That is, serving your app built in production mode. 6 | * 7 | * Feel free to modify this file, but don't remove it! 8 | * 9 | * Learn more about Vite's preview command: 10 | * - https://vitejs.dev/config/preview-options.html#preview-options 11 | * 12 | */ 13 | import { createQwikCity } from '@builder.io/qwik-city/middleware/node'; 14 | import qwikCityPlan from '@qwik-city-plan'; 15 | import render from './entry.ssr'; 16 | 17 | /** 18 | * The default export is the QwikCity adapter used by Vite preview. 19 | */ 20 | export default createQwikCity({ render, qwikCityPlan }); 21 | -------------------------------------------------------------------------------- /src/functions/questions.ts: -------------------------------------------------------------------------------- 1 | import { server$ } from '@builder.io/qwik-city'; 2 | import { prismaClient } from '~/constants/prisma-client'; 3 | 4 | const workflowId = 'test'; 5 | 6 | export const addQuestion = server$( 7 | async (question: string, answer: string, workflow = workflowId) => { 8 | return await prismaClient!.answers.create({ 9 | data: { 10 | question, 11 | answer, 12 | workflow_id: workflow, 13 | }, 14 | }); 15 | } 16 | ); 17 | 18 | export const getQuestions = server$(async (workflow = workflowId) => { 19 | return await prismaClient!.answers.findMany({ 20 | where: { 21 | workflow_id: workflow, 22 | }, 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/routes/service-worker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * The service-worker.ts file is used to have state of the art prefetching. 5 | * https://qwik.builder.io/qwikcity/prefetching/overview/ 6 | * 7 | * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline. 8 | * You can also use this file to add more functionality that runs in the service worker. 9 | */ 10 | import { setupServiceWorker } from '@builder.io/qwik-city/service-worker'; 11 | 12 | setupServiceWorker(); 13 | 14 | addEventListener('install', () => self.skipWaiting()); 15 | 16 | addEventListener('activate', () => self.clients.claim()); 17 | 18 | declare const self: ServiceWorkerGlobalScope; 19 | -------------------------------------------------------------------------------- /src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from 'puppeteer'; 2 | // import github from './github'; 3 | import exec from './exec'; 4 | 5 | type PluginContext = { 6 | page?: Page; 7 | }; 8 | 9 | export type PluginAction< 10 | T extends Record, 11 | Name extends string 12 | > = { 13 | name: Name; 14 | description: string; 15 | example?: T; 16 | handler: (info: { 17 | action: T & { action: Name }; 18 | context: PluginContext; 19 | }) => Promise; 20 | }; 21 | 22 | export type Plugin = { 23 | name: string; 24 | requires?: string[]; 25 | promptInfo?: string; 26 | actions: PluginAction[]; 27 | }; 28 | 29 | export const plugins: Plugin[] = [exec()].filter(Boolean); 30 | -------------------------------------------------------------------------------- /src/functions/remove-nth-query-params.ts: -------------------------------------------------------------------------------- 1 | export function removeNthQueryParams(url: string, n: number) { 2 | // Parse the URL using the URL constructor 3 | const parsedUrl = new URL(url); 4 | 5 | // Get the search parameters from the parsed URL 6 | const searchParams = parsedUrl.searchParams; 7 | 8 | // Convert the search parameters to an array of key-value pairs 9 | const paramsArray = Array.from(searchParams.entries()); 10 | 11 | // Clear all existing search parameters 12 | searchParams.forEach((value, key) => { 13 | searchParams.delete(key); 14 | }); 15 | 16 | // Add back only the first n query parameters 17 | for (let i = 0; i < Math.min(n, paramsArray.length); i++) { 18 | const [key, value] = paramsArray[i]; 19 | searchParams.append(key, value); 20 | } 21 | 22 | return parsedUrl.href; 23 | } 24 | -------------------------------------------------------------------------------- /src/entry.ssr.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * WHAT IS THIS FILE? 3 | * 4 | * SSR entry point, in all cases the application is render outside the browser, this 5 | * entry point will be the common one. 6 | * 7 | * - Server (express, cloudflare...) 8 | * - npm run start 9 | * - npm run preview 10 | * - npm run build 11 | * 12 | */ 13 | import { 14 | renderToStream, 15 | type RenderToStreamOptions, 16 | } from '@builder.io/qwik/server'; 17 | import { manifest } from '@qwik-client-manifest'; 18 | import Root from './root'; 19 | 20 | export default function (opts: RenderToStreamOptions) { 21 | return renderToStream(, { 22 | manifest, 23 | ...opts, 24 | // Use container attributes to set attributes on the html tag. 25 | containerAttributes: { 26 | lang: 'en-us', 27 | ...opts.containerAttributes, 28 | }, 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2020", 6 | "lib": ["es2020", "DOM", "WebWorker", "DOM.Iterable"], 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@builder.io/qwik", 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "resolveJsonModule": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true, 14 | "skipLibCheck": true, 15 | "incremental": true, 16 | "isolatedModules": true, 17 | "outDir": "tmp", 18 | "noEmit": true, 19 | "types": ["node", "vite/client"], 20 | "paths": { 21 | "~/*": ["./src/*"] 22 | } 23 | }, 24 | "files": ["./.eslintrc.cjs", "src/functions/stream-completion.tsx", "src/functions/remove-nth-query-params.ts", "src/functions/get-browser-state.ts"], 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /src/routes/api/v1/run/index.ts: -------------------------------------------------------------------------------- 1 | import type { RequestEvent } from '@builder.io/qwik-city'; 2 | import { z } from '@builder.io/qwik-city'; 3 | import { attempt } from '../ai'; 4 | import { runAction } from '~/functions/run-action'; 5 | 6 | const schema = z.object({ 7 | action: z.object({ 8 | id: z.string(), 9 | data: z.any(), 10 | }), 11 | persist: z.boolean().optional(), 12 | }); 13 | 14 | export const onPost = async (request: RequestEvent) => { 15 | const body = await request.parseBody(); 16 | const values = attempt(() => schema.parse(body)); 17 | if (values instanceof Error) { 18 | return request.json(401, { error: values.message }); 19 | } 20 | const { action, persist } = values; 21 | const result = await runAction( 22 | { 23 | data: action.data, 24 | id: BigInt(action.id), 25 | }, 26 | persist 27 | ); 28 | return request.json(200, { result }); 29 | }; 30 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/root.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | import { 3 | QwikCityProvider, 4 | RouterOutlet, 5 | ServiceWorkerRegister, 6 | } from '@builder.io/qwik-city'; 7 | import { RouterHead } from './components/router-head/router-head'; 8 | import { config } from 'dotenv'; 9 | config(); 10 | 11 | import './global.css'; 12 | 13 | export default component$(() => { 14 | /** 15 | * The root of a QwikCity site always start with the component, 16 | * immediately followed by the document's and . 17 | * 18 | * Dont remove the `` and `` elements. 19 | */ 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | }); 35 | -------------------------------------------------------------------------------- /src/components/router-head/router-head.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | import { useDocumentHead, useLocation } from '@builder.io/qwik-city'; 3 | 4 | /** 5 | * The RouterHead component is placed inside of the document `` element. 6 | */ 7 | export const RouterHead = component$(() => { 8 | const head = useDocumentHead(); 9 | const loc = useLocation(); 10 | 11 | return ( 12 | <> 13 | {head.title} 14 | 15 | 16 | 17 | 18 | 19 | {head.meta.map((m) => ( 20 | 21 | ))} 22 | 23 | {head.links.map((l) => ( 24 | 25 | ))} 26 | 27 | {head.styles.map((s) => ( 28 |