Unified · Split
| @@ -35,7 +35,7 @@ const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfig.ServerConfig[ · 34 unmodified lines | ||
| 35 | 35 | EnvironmentAuth.layer.pipe( |
| 36 | 36 | Layer.provide(SqlitePersistenceMemory), |
| 37 | 37 | Layer.provide(ServerSecretStore.layer), |
| 38 | − Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 38 | + Layer.provide(ServerEnvironment.identityLayer), | |
| 39 | 39 | Layer.provide(makeServerConfigLayer(overrides)), |
| 40 | 40 | ); |
| 41 | 41 | |
| @@ -1023,5 +1023,5 @@ export const storageLayer = Layer.mergeAll(ServerSecretStore.layer, SqlitePersis · 1022 unmodified lines | ||
| 1023 | 1023 | |
| 1024 | 1024 | export const runtimeLayer = layer.pipe( |
| 1025 | 1025 | Layer.provideMerge(storageLayer), |
| 1026 | − Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 1026 | + Layer.provideMerge(ServerEnvironment.identityLayer), | |
| 1027 | 1027 | ); |
| @@ -36,7 +36,7 @@ const makeEnvironmentAuthLayer = ( · 35 unmodified lines | ||
| 36 | 36 | EnvironmentAuth.layer.pipe( |
| 37 | 37 | Layer.provideMerge(ServerSecretStore.layer), |
| 38 | 38 | Layer.provideMerge(SqlitePersistenceMemory), |
| 39 | − Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 39 | + Layer.provide(ServerEnvironment.identityLayer), | |
| 40 | 40 | Layer.provide(makeServerConfigLayer(overrides)), |
| 41 | 41 | ); |
| 42 | 42 | |
| @@ -6,13 +6,12 @@ import * as Layer from "effect/Layer"; · 5 unmodified lines | ||
| 6 | 6 | import * as ServerConfig from "../config.ts"; |
| 7 | 7 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 8 | 8 | import * as EnvironmentAuthPolicy from "./EnvironmentAuthPolicy.ts"; |
| 9 | −import * as ServerSecretStore from "./ServerSecretStore.ts"; | |
| 10 | 9 | |
| 11 | 10 | const makeEnvironmentAuthPolicyLayer = ( |
| 12 | 11 | overrides?: Partial<ServerConfig.ServerConfig["Service"]>, |
| 13 | 12 | ) => |
| 14 | 13 | EnvironmentAuthPolicy.layer.pipe( |
| 15 | − Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 14 | + Layer.provide(ServerEnvironment.identityLayer), | |
| 16 | 15 | Layer.provide( |
| 17 | 16 | Layer.effect( |
| 18 | 17 | ServerConfig.ServerConfig, |
| @@ -16,7 +16,7 @@ export class EnvironmentAuthPolicy extends Context.Service< · 15 unmodified lines | ||
| 16 | 16 | |
| 17 | 17 | export const make = Effect.gen(function* () { |
| 18 | 18 | const config = yield* ServerConfig.ServerConfig; |
| 19 | − const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 19 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironmentIdentity; | |
| 20 | 20 | const isRemoteReachable = isRemoteReachableHost(config.host); |
| 21 | 21 | |
| 22 | 22 | const policy = |
| @@ -28,9 +28,8 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se · 27 unmodified lines | ||
| 28 | 28 | ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-session-test-" }))); |
| 29 | 29 | |
| 30 | 30 | const makeServerEnvironmentLayer = (environmentId: EnvironmentId) => |
| 31 | − Layer.succeed(ServerEnvironment.ServerEnvironment, { | |
| 31 | + Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, { | |
| 32 | 32 | getEnvironmentId: Effect.succeed(environmentId), |
| 33 | − getDescriptor: Effect.die(new Error("unused in SessionStore tests")), | |
| 34 | 33 | }); |
| 35 | 34 | |
| 36 | 35 | const makeSessionStoreLayer = ( |
| @@ -473,7 +473,7 @@ function toAuthClientSession(input: Omit<AuthClientSession, "current">): AuthCli · 472 unmodified lines | ||
| 473 | 473 | export const make = Effect.gen(function* () { |
| 474 | 474 | const crypto = yield* Crypto.Crypto; |
| 475 | 475 | const serverConfig = yield* ServerConfig.ServerConfig; |
| 476 | − const serverEnvironment = yield* ServerEnvironment.ServerEnvironment; | |
| 476 | + const serverEnvironment = yield* ServerEnvironment.ServerEnvironmentIdentity; | |
| 477 | 477 | const secretStore = yield* ServerSecretStore.ServerSecretStore; |
| 478 | 478 | const authSessions = yield* AuthSessions.AuthSessionRepository; |
| 479 | 479 | const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32); |
| @@ -13,6 +13,7 @@ import { · 12 unmodified lines | ||
| 13 | 13 | ThreadId, |
| 14 | 14 | } from "@t3tools/contracts"; |
| 15 | 15 | import * as NetService from "@t3tools/shared/Net"; |
| 16 | +import { HostProcessEnvironment } from "@t3tools/shared/hostProcess"; | |
| 16 | 17 | import { assert, it } from "@effect/vitest"; |
| 17 | 18 | import * as Effect from "effect/Effect"; |
| 18 | 19 | import * as DateTime from "effect/DateTime"; |
| @@ -26,6 +27,11 @@ import * as TestConsole from "effect/testing/TestConsole"; · 7 unmodified lines | ||
| 26 | 27 | import { Command } from "effect/unstable/cli"; |
| 27 | 28 | |
| 28 | 29 | import { cli, makeCli } from "./bin.ts"; |
| 30 | +import * as ServiceLauncherClient from "./cloud/serviceLauncherClient.ts"; | |
| 31 | +import { | |
| 32 | + SERVICE_LAUNCHER_CONTEXT_ENV, | |
| 33 | + SERVICE_LAUNCHER_PROTOCOL, | |
| 34 | +} from "./cloud/serviceProtocol.ts"; | |
| 29 | 35 | import * as ServerConfig from "./config.ts"; |
| 30 | 36 | import * as ServerEnvironment from "./environment/ServerEnvironment.ts"; |
| 31 | 37 | import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts"; |
| @@ -43,7 +49,24 @@ import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; · 11 unmodified lines | ||
| 43 | 49 | import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; |
| 44 | 50 | import { environmentAuthenticatedAuthLayer } from "./auth/http.ts"; |
| 45 | 51 | |
| 52 | +import packageJson from "../package.json" with { type: "json" }; | |
| 53 | + | |
| 46 | 54 | const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer); |
| 55 | +const DisconnectedLauncherChildLayer = Layer.mergeAll( | |
| 56 | + Layer.succeed(HostProcessEnvironment, { | |
| 57 | + ...process.env, | |
| 58 | + [SERVICE_LAUNCHER_CONTEXT_ENV]: JSON.stringify({ | |
| 59 | + protocol: SERVICE_LAUNCHER_PROTOCOL, | |
| 60 | + childVersion: packageJson.version, | |
| 61 | + }), | |
| 62 | + }), | |
| 63 | + Layer.succeed(ServiceLauncherClient.ServiceLauncherHostProcess, { | |
| 64 | + connected: false, | |
| 65 | + send: () => false, | |
| 66 | + on: () => undefined, | |
| 67 | + off: () => undefined, | |
| 68 | + }), | |
| 69 | +); | |
| 47 | 70 | class ProjectCliHttpApi extends HttpApi.make("environment").add(EnvironmentOrchestrationHttpApi) {} |
| 48 | 71 | |
| 49 | 72 | const connectCli = makeCli({ cloudEnabled: true }); |
| @@ -128,7 +151,7 @@ const withLiveProjectCliServer = <A, E, R>(baseDir: string, run: () => Effect.Ef · 78 unmodified lines | ||
| 128 | 151 | Layer.provideMerge( |
| 129 | 152 | EnvironmentAuth.layer.pipe( |
| 130 | 153 | Layer.provideMerge(SqlitePersistenceLayerLive), |
| 131 | − Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))), | |
| 154 | + Layer.provide(ServerEnvironment.identityLayer), | |
| 132 | 155 | Layer.provide(ServerSecretStore.layer), |
| 133 | 156 | ), |
| 134 | 157 | ), |
| @@ -239,7 +262,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 104 unmodified lines | ||
| 239 | 262 | assert.equal(status.linked, false); |
| 240 | 263 | assert.equal(status.cloudUserId, null); |
| 241 | 264 | assert.equal(status.relayUrl, null); |
| 242 | − }), | |
| 265 | + }).pipe(Effect.provide(DisconnectedLauncherChildLayer)), | |
| 243 | 266 | ); |
| 244 | 267 | |
| 245 | 268 | it.effect("reports actionable human-readable headless connect state", () => |
| @@ -410,7 +433,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 164 unmodified lines | ||
| 410 | 433 | "relay:write", |
| 411 | 434 | ]); |
| 412 | 435 | assert.equal("token" in (listed[0] ?? {}), false); |
| 413 | − }), | |
| 436 | + }).pipe(Effect.provide(DisconnectedLauncherChildLayer)), | |
| 414 | 437 | ); |
| 415 | 438 | |
| 416 | 439 | it.effect("rejects invalid ttl values before running auth commands", () => |
| @@ -337,7 +337,7 @@ const unlinkRelayEnvironment = Effect.fn("cloud.cli.unlink_relay_environment")(f · 336 unmodified lines | ||
| 337 | 337 | return { status: "not-authenticated" } satisfies RelayUnlinkResult; |
| 338 | 338 | } |
| 339 | 339 | |
| 340 | − const environment = yield* ServerEnvironment.ServerEnvironment; | |
| 340 | + const environment = yield* ServerEnvironment.ServerEnvironmentIdentity; | |
| 341 | 341 | const environmentId = yield* environment.getEnvironmentId; |
| 342 | 342 | const relayUrl = yield* relayUrlConfig; |
| 343 | 343 | const httpClient = yield* HttpClient.HttpClient; |
| @@ -432,7 +432,7 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E · 88 unmodified lines | ||
| 432 | 432 | | HttpClient.HttpClient |
| 433 | 433 | | Prompt.Environment |
| 434 | 434 | | ServerConfig.ServerConfig |
| 435 | − | ServerEnvironment.ServerEnvironment | |
| 435 | + | ServerEnvironment.ServerEnvironmentIdentity | |
| 436 | 436 | >, |
| 437 | 437 | options?: { |
| 438 | 438 | readonly quietLogs?: boolean; |
| @@ -449,7 +449,6 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E · 10 unmodified lines | ||
| 449 | 449 | ), |
| 450 | 450 | RelayClient.layerCloudflared({ baseDir: config.baseDir }), |
| 451 | 451 | EnvironmentAuth.runtimeLayer, |
| 452 | − ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer)), | |
| 453 | 452 | bootServiceLayer(config), |
| 454 | 453 | headlessRelayClientTracingLayer, |
| 455 | 454 | ).pipe( |
| @@ -6,6 +6,7 @@ import * as NodePath from "node:path"; · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 8 | 8 | import * as NetService from "@t3tools/shared/Net"; |
| 9 | +import { HostProcessEnvironment } from "@t3tools/shared/hostProcess"; | |
| 9 | 10 | import { assert, describe, expect, it } from "@effect/vitest"; |
| 10 | 11 | import * as Effect from "effect/Effect"; |
| 11 | 12 | import * as Layer from "effect/Layer"; |
| @@ -13,6 +14,11 @@ import * as TestConsole from "effect/testing/TestConsole"; · 1 unmodified line | ||
| 13 | 14 | import { Command } from "effect/unstable/cli"; |
| 14 | 15 | |
| 15 | 16 | import { cli } from "../bin.ts"; |
| 17 | +import { | |
| 18 | + SERVICE_LAUNCHER_CONTEXT_ENV, | |
| 19 | + SERVICE_LAUNCHER_PROTOCOL, | |
| 20 | +} from "../cloud/serviceProtocol.ts"; | |
| 21 | +import * as ServiceLauncherClient from "../cloud/serviceLauncherClient.ts"; | |
| 16 | 22 | import { |
| 17 | 23 | makePersistedServerRuntimeState, |
| 18 | 24 | persistServerRuntimeState, |
| @@ -24,6 +30,8 @@ import { · 5 unmodified lines | ||
| 24 | 30 | resolveTailscaleLocalTarget, |
| 25 | 31 | } from "./pair.ts"; |
| 26 | 32 | |
| 33 | +import packageJson from "../../package.json" with { type: "json" }; | |
| 34 | + | |
| 27 | 35 | const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer); |
| 28 | 36 | |
| 29 | 37 | const baseState = { |
| @@ -170,7 +178,22 @@ describe("t3 pair", () => { · 140 unmodified lines | ||
| 170 | 178 | assert.equal(credentials.length, 1); |
| 171 | 179 | assert.equal(credentials[0]?.label, "t3 pair"); |
| 172 | 180 | }), |
| 173 | − ).pipe(Effect.provide(NodeServices.layer)), | |
| 181 | + ).pipe( | |
| 182 | + Effect.provide(NodeServices.layer), | |
| 183 | + Effect.provideService(HostProcessEnvironment, { | |
| 184 | + ...process.env, | |
| 185 | + [SERVICE_LAUNCHER_CONTEXT_ENV]: JSON.stringify({ | |
| 186 | + protocol: SERVICE_LAUNCHER_PROTOCOL, | |
| 187 | + childVersion: packageJson.version, | |
| 188 | + }), | |
| 189 | + }), | |
| 190 | + Effect.provideService(ServiceLauncherClient.ServiceLauncherHostProcess, { | |
| 191 | + connected: false, | |
| 192 | + send: () => false, | |
| 193 | + on: () => undefined, | |
| 194 | + off: () => undefined, | |
| 195 | + }), | |
| 196 | + ), | |
| 174 | 197 | ); |
| 175 | 198 | |
| 176 | 199 | it.effect("pairs through the recorded dev web URL for dev servers", () => |
| @@ -42,6 +42,13 @@ export class ServerEnvironment extends Context.Service< · 41 unmodified lines | ||
| 42 | 42 | } |
| 43 | 43 | >()("t3/environment/ServerEnvironment") {} |
| 44 | 44 | |
| 45 | +export class ServerEnvironmentIdentity extends Context.Service< | |
| 46 | + ServerEnvironmentIdentity, | |
| 47 | + { | |
| 48 | + readonly getEnvironmentId: Effect.Effect<EnvironmentId>; | |
| 49 | + } | |
| 50 | +>()("t3/environment/ServerEnvironment/ServerEnvironmentIdentity") {} | |
| 51 | + | |
| 45 | 52 | function platformOs(platform: NodeJS.Platform): ExecutionEnvironmentDescriptor["platform"]["os"] { |
| 46 | 53 | switch (platform) { |
| 47 | 54 | case "darwin": |
| @@ -68,14 +75,10 @@ function platformArch( · 20 unmodified lines | ||
| 68 | 75 | } |
| 69 | 76 | } |
| 70 | 77 | |
| 71 | −export const make = Effect.gen(function* () { | |
| 78 | +const makeIdentity = Effect.gen(function* () { | |
| 72 | 79 | const fileSystem = yield* FileSystem.FileSystem; |
| 73 | − const path = yield* Path.Path; | |
| 74 | 80 | const serverConfig = yield* ServerConfig.ServerConfig; |
| 75 | − const secrets = yield* ServerSecretStore.ServerSecretStore; | |
| 76 | 81 | const crypto = yield* Crypto.Crypto; |
| 77 | − const hostPlatform = yield* HostProcessPlatform; | |
| 78 | − const hostArchitecture = yield* HostProcessArchitecture; | |
| 79 | 82 | |
| 80 | 83 | const readPersistedEnvironmentId = Effect.gen(function* () { |
| 81 | 84 | const exists = yield* fileSystem.exists(serverConfig.environmentIdPath).pipe( |
| @@ -131,6 +134,19 @@ export const make = Effect.gen(function* () { · 49 unmodified lines | ||
| 131 | 134 | }); |
| 132 | 135 | |
| 133 | 136 | const environmentId = EnvironmentId.make(environmentIdRaw); |
| 137 | + return ServerEnvironmentIdentity.of({ | |
| 138 | + getEnvironmentId: Effect.succeed(environmentId), | |
| 139 | + }); | |
| 140 | +}); | |
| 141 | + | |
| 142 | +export const make = Effect.gen(function* () { | |
| 143 | + const path = yield* Path.Path; | |
| 144 | + const serverConfig = yield* ServerConfig.ServerConfig; | |
| 145 | + const secrets = yield* ServerSecretStore.ServerSecretStore; | |
| 146 | + const identity = yield* ServerEnvironmentIdentity; | |
| 147 | + const hostPlatform = yield* HostProcessPlatform; | |
| 148 | + const hostArchitecture = yield* HostProcessArchitecture; | |
| 149 | + const environmentId = yield* identity.getEnvironmentId; | |
| 134 | 150 | const cwdBaseName = path.basename(serverConfig.cwd).trim(); |
| 135 | 151 | const label = yield* resolveServerEnvironmentLabel({ cwdBaseName }); |
| 136 | 152 | const launcher = yield* resolveServiceLauncherMode(); |
| @@ -185,4 +201,9 @@ export const make = Effect.gen(function* () { · 48 unmodified lines | ||
| 185 | 201 | * provide the external platform services, a ServerConfig, and the |
| 186 | 202 | * ServerSecretStore backing the descriptor's publishing capability. |
| 187 | 203 | */ |
| 188 | −export const layer = Layer.effect(ServerEnvironment, make).pipe(Layer.provide(ProcessRunner.layer)); | |
| 204 | +export const identityLayer = Layer.effect(ServerEnvironmentIdentity, makeIdentity); | |
| 205 | + | |
| 206 | +export const layer = Layer.effect(ServerEnvironment, make).pipe( | |
| 207 | + Layer.provideMerge(identityLayer), | |
| 208 | + Layer.provide(ProcessRunner.layer), | |
| 209 | +); | |
| @@ -292,9 +292,8 @@ const makeAuthTestLayer = () => · 291 unmodified lines | ||
| 292 | 292 | Layer.provide(SqlitePersistenceMemory), |
| 293 | 293 | Layer.provide(ServerSecretStore.layer), |
| 294 | 294 | Layer.provide( |
| 295 | − Layer.mock(ServerEnvironment.ServerEnvironment)({ | |
| 295 | + Layer.mock(ServerEnvironment.ServerEnvironmentIdentity)({ | |
| 296 | 296 | getEnvironmentId: Effect.succeed(testEnvironmentDescriptor.environmentId), |
| 297 | − getDescriptor: Effect.succeed(testEnvironmentDescriptor), | |
| 298 | 297 | }), |
| 299 | 298 | ), |
| 300 | 299 | ); |