(options: LocalExecutorOptions = {})
| 143 | ); |
| 144 | |
| 145 | const createLocalExecutorLayer = (options: LocalExecutorOptions = {}) => { |
| 146 | const storage = resolveStorage(); |
| 147 | |
| 148 | return Layer.effect(LocalExecutorTag)( |
| 149 | Effect.gen(function* () { |
| 150 | const { cwd, plugins } = yield* loadLocalPlugins(options); |
| 151 | const tenantId = makeTenantId(cwd); |
| 152 | const tables = collectTables(); |
| 153 | |
| 154 | const owned = yield* Effect.acquireRelease( |
| 155 | Effect.tryPromise({ |
| 156 | try: () => |
| 157 | openOwnedLocalDatabase({ |
| 158 | dataDir: storage.dataDir, |
| 159 | tables, |
| 160 | namespace: localNamespace, |
| 161 | tenantId, |
| 162 | }), |
| 163 | catch: (cause) => |
| 164 | new LocalExecutorCreateError({ |
| 165 | message: CREATE_SQLITE_ERROR_MESSAGE, |
| 166 | cause, |
| 167 | }), |
| 168 | }), |
| 169 | (database) => Effect.promise(() => database.close()).pipe(Effect.ignore), |
| 170 | ); |
| 171 | const sqlite = owned.db; |
| 172 | const migration = owned.migration; |
| 173 | |
| 174 | // Boot-time data migrations: each registry entry runs once and is |
| 175 | // stamped in the `data_migration` ledger; stamped entries are skipped |
| 176 | // without touching the data. |
| 177 | yield* runSqliteDataMigrations(sqlite.client, localDataMigrations).pipe( |
| 178 | Effect.mapError( |
| 179 | (cause) => |
| 180 | new LocalExecutorCreateError({ |
| 181 | message: CREATE_SQLITE_ERROR_MESSAGE, |
| 182 | cause, |
| 183 | }), |
| 184 | ), |
| 185 | ); |
| 186 | |
| 187 | // webBaseUrl is where the executor's web UI listens — same port as the |
| 188 | // daemon API since the daemon serves both. Mirrors serve.ts's port |
| 189 | // resolution so a custom $PORT flows through. EXECUTOR_WEB_BASE_URL |
| 190 | // overrides entirely for deployments where the UI is on a different host. |
| 191 | const webBaseUrl = |
| 192 | process.env.EXECUTOR_WEB_BASE_URL ?? `http://localhost:${process.env.PORT ?? "4788"}`; |
| 193 | |
| 194 | const executor = yield* createExecutor({ |
| 195 | tenant: Tenant.make(tenantId), |
| 196 | subject: Subject.make(LOCAL_SUBJECT), |
| 197 | db: sqlite.db, |
| 198 | plugins, |
| 199 | onIntegrationChange: (event) => |
| 200 | localAnalytics.record( |
| 201 | event.kind === "added" ? "integration_added" : "integration_removed", |
| 202 | { plugin_key: event.pluginKey }, |
no test coverage detected