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