diff --git a/packages/api/config.mts b/packages/api/config.mts index d68d4d6d..2b399130 100644 --- a/packages/api/config.mts +++ b/packages/api/config.mts @@ -11,6 +11,7 @@ async function init() { const defaultConfig = { baseDir: HOME_DIR, defaultLanguage: 'typescript', + autoInstallDependencies: true, installId: randomid(), aiConfig: { provider: 'openai', model: 'gpt-4o' } as const, aiProvider: 'openai', diff --git a/packages/api/db/schema.mts b/packages/api/db/schema.mts index 1bbf9783..1e3a89a0 100644 --- a/packages/api/db/schema.mts +++ b/packages/api/db/schema.mts @@ -8,6 +8,9 @@ export const configs = sqliteTable('config', { defaultLanguage: text('default_language').notNull().default('typescript'), openaiKey: text('openai_api_key'), anthropicKey: text('anthropic_api_key'), + autoInstallDependencies: integer('auto_install_dependencies', { mode: 'boolean' }) + .notNull() + .default(true), // TODO: This is deprecated in favor of SRCBOOK_DISABLE_ANALYTICS env variable. Remove this. enabledAnalytics: integer('enabled_analytics', { mode: 'boolean' }).notNull().default(true), // Stable ID for posthog diff --git a/packages/api/drizzle/0012_add_auto_install_dependency.sql b/packages/api/drizzle/0012_add_auto_install_dependency.sql new file mode 100644 index 00000000..6562513a --- /dev/null +++ b/packages/api/drizzle/0012_add_auto_install_dependency.sql @@ -0,0 +1 @@ +ALTER TABLE `config` ADD `auto_install_dependencies` integer DEFAULT true NOT NULL; \ No newline at end of file diff --git a/packages/api/drizzle/meta/0011_snapshot.json b/packages/api/drizzle/meta/0011_snapshot.json new file mode 100644 index 00000000..221f84c4 --- /dev/null +++ b/packages/api/drizzle/meta/0011_snapshot.json @@ -0,0 +1,261 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "b16851ac-e597-411d-9b50-df46ad9d2c99", + "prevId": "aeb418fb-06df-4fc2-8afc-f18d95014b46", + "tables": { + "apps": { + "name": "apps", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "apps_external_id_unique": { + "name": "apps_external_id_unique", + "columns": [ + "external_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "config": { + "name": "config", + "columns": { + "base_dir": { + "name": "base_dir", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_language": { + "name": "default_language", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'typescript'" + }, + "openai_api_key": { + "name": "openai_api_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "anthropic_api_key": { + "name": "anthropic_api_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auto_install_dependencies": { + "name": "auto_install_dependencies", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "enabled_analytics": { + "name": "enabled_analytics", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "srcbook_installation_id": { + "name": "srcbook_installation_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mdbjec3psib2rf35a3t3bhsibg'" + }, + "ai_provider": { + "name": "ai_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'openai'" + }, + "ai_model": { + "name": "ai_model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'gpt-4o'" + }, + "ai_base_url": { + "name": "ai_base_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_email": { + "name": "subscription_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "secrets": { + "name": "secrets", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "secrets_name_unique": { + "name": "secrets_name_unique", + "columns": [ + "name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "secrets_to_sessions": { + "name": "secrets_to_sessions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secret_id": { + "name": "secret_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "secrets_to_sessions_session_id_secret_id_unique": { + "name": "secrets_to_sessions_session_id_secret_id_unique", + "columns": [ + "session_id", + "secret_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "secrets_to_sessions_secret_id_secrets_id_fk": { + "name": "secrets_to_sessions_secret_id_secrets_id_fk", + "tableFrom": "secrets_to_sessions", + "tableTo": "secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/api/drizzle/meta/0012_snapshot.json b/packages/api/drizzle/meta/0012_snapshot.json new file mode 100644 index 00000000..918ebd6a --- /dev/null +++ b/packages/api/drizzle/meta/0012_snapshot.json @@ -0,0 +1,261 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "68788c55-0d37-4edd-baae-a1b3164de32d", + "prevId": "b16851ac-e597-411d-9b50-df46ad9d2c99", + "tables": { + "apps": { + "name": "apps", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "apps_external_id_unique": { + "name": "apps_external_id_unique", + "columns": [ + "external_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "config": { + "name": "config", + "columns": { + "base_dir": { + "name": "base_dir", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_language": { + "name": "default_language", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'typescript'" + }, + "openai_api_key": { + "name": "openai_api_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "anthropic_api_key": { + "name": "anthropic_api_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auto_install_dependencies": { + "name": "auto_install_dependencies", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "enabled_analytics": { + "name": "enabled_analytics", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "srcbook_installation_id": { + "name": "srcbook_installation_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'lpjreo2lm2bs1q7sk5jbl83ldc'" + }, + "ai_provider": { + "name": "ai_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'openai'" + }, + "ai_model": { + "name": "ai_model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'gpt-4o'" + }, + "ai_base_url": { + "name": "ai_base_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_email": { + "name": "subscription_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "secrets": { + "name": "secrets", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "secrets_name_unique": { + "name": "secrets_name_unique", + "columns": [ + "name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "secrets_to_sessions": { + "name": "secrets_to_sessions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secret_id": { + "name": "secret_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "secrets_to_sessions_session_id_secret_id_unique": { + "name": "secrets_to_sessions_session_id_secret_id_unique", + "columns": [ + "session_id", + "secret_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "secrets_to_sessions_secret_id_secrets_id_fk": { + "name": "secrets_to_sessions_secret_id_secrets_id_fk", + "tableFrom": "secrets_to_sessions", + "tableTo": "secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/api/drizzle/meta/_journal.json b/packages/api/drizzle/meta/_journal.json index 73cc4049..814705dc 100644 --- a/packages/api/drizzle/meta/_journal.json +++ b/packages/api/drizzle/meta/_journal.json @@ -78,6 +78,20 @@ "when": 1726808187994, "tag": "0010_create_apps", "breakpoints": true + }, + { + "idx": 11, + "version": "6", + "when": 1728457840317, + "tag": "0011_apps_external_id_unique", + "breakpoints": true + }, + { + "idx": 12, + "version": "6", + "when": 1728462057807, + "tag": "0012_add_auto_install_dependency", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/web/src/routes/session.tsx b/packages/web/src/routes/session.tsx index c40a95a9..1bdf18a2 100644 --- a/packages/web/src/routes/session.tsx +++ b/packages/web/src/routes/session.tsx @@ -30,6 +30,7 @@ import { SessionNavbar } from '@/components/navbar'; import { toast } from 'sonner'; import { TsConfigProvider } from '@/components/use-tsconfig-json'; import { VITE_SRCBOOK_DEBUG_RENDER_SESSION_AS_READ_ONLY } from '@/lib/environment'; +import { useSettings } from '@/components/use-settings'; async function loader({ params }: LoaderFunctionArgs) { const [{ result: config }, { result: srcbooks }, { result: session }] = await Promise.all([ @@ -134,14 +135,14 @@ function Session(props: SessionProps) { setTsServerDiagnostics, setTsServerSuggestions, } = useCells(); - + const { autoInstallDependencies } = useSettings(); const { npmInstall, failed: dependencyInstallFailed, outdated: outdatedDependencies, installing: installingDependencies, } = usePackageJson(); - + const [initialInstallAttempt, setInitialInstallAttempt] = useState(false); const [depsInstallModalOpen, setDepsInstallModalOpen] = useState(false); const [[selectedPanelName, selectedPanelOpen], setSelectedPanelNameAndOpen] = useState< [Panel['name'], boolean] @@ -293,7 +294,9 @@ function Session(props: SessionProps) { if (depsInstallModalOpen || isPanelOpen('packages')) { return result; } - + if (!initialInstallAttempt) { + return; + } if (installingDependencies) { const toastId = toast.loading('Installing dependencies...'); result = () => toast.dismiss(toastId); @@ -331,6 +334,7 @@ function Session(props: SessionProps) { return result; }, [ + initialInstallAttempt, outdatedDependencies, installingDependencies, dependencyInstallFailed, @@ -339,6 +343,16 @@ function Session(props: SessionProps) { npmInstall, ]); + useEffect(() => { + const handleAutoInstallation = () => { + if (autoInstallDependencies) { + npmInstall(); + } + setInitialInstallAttempt(true); + }; + setTimeout(() => handleAutoInstallation(), 1500); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return (