bravenlab 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -2
- package/dist/auth.js +651 -0
- package/dist/auth.js.map +7 -0
- package/dist/bin.js +14492 -0
- package/dist/bin.js.map +7 -0
- package/dist/cli.js +8978 -0
- package/dist/cli.js.map +7 -0
- package/package.json +54 -4
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/auth.ts", "../../identity-store/src/environment.ts", "../../identity-store/src/errors.ts", "../../identity-store/src/identity.ts", "../../identity-store/src/login.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n Cause,\n Console,\n Duration,\n Effect,\n Match,\n Option,\n Ref,\n Schedule,\n} from \"effect\";\nimport { HttpClient, HttpClientRequest, HttpClientResponse } from \"effect/http\";\nimport * as Schema from \"effect/Schema\";\nimport * as Tuple from \"effect/Tuple\";\nimport {\n CliFailure,\n EXIT_FAILURE,\n fail,\n oneLine,\n} from \"@braven/identity-store\";\nimport { Browser } from \"@braven/identity-store/login\";\n\n/**\n * The browser sign-in lives in `@braven/identity-store/login` so the watcher\n * shares its one implementation; this module keeps the CLI's device-grant flow\n * and re-exports the shared pieces for the CLI's existing imports.\n */\nexport {\n awaitToken,\n browserLayer,\n LOGIN_TIMEOUT,\n lookupEmail,\n rememberSession,\n Sessions,\n sessionsLayer,\n signIn,\n SignInFailure,\n SignInReason,\n type LoopbackToken,\n} from \"@braven/identity-store/login\";\nexport { Browser };\n\n/** RFC 8628's device-code grant type; better-auth rejects anything else. */\nexport const DEVICE_GRANT_TYPE = \"urn:ietf:params:oauth:grant-type:device_code\";\n\n/** The scopes a device sign-in asks for; the grant only needs a session. */\nexport const DEVICE_SCOPE = \"openid profile email\";\n\n/** RFC 8628 defaults when the server omits `expires_in` / `interval`. */\nconst DEVICE_CODE_LIFETIME = Duration.minutes(30);\nconst DEVICE_POLL_INTERVAL = Duration.seconds(5);\n\n/** RFC 8628 \u00A73.5: every `slow_down` adds five seconds to the interval. */\nconst DEVICE_SLOW_DOWN = Duration.seconds(5);\n\n/**\n * The device grant both endpoints live at, under better-auth's base path \u2014\n * the same `/api/auth` the loopback flow proves its token against.\n */\nconst deviceUrl = (origin: string, endpoint: \"code\" | \"token\"): string =>\n `${origin}/api/auth/device/${endpoint}`;\n\n/** What `POST /device/code` answers: the codes, where to type them, and when. */\nexport class DeviceCode extends Schema.Class<DeviceCode>(\"DeviceCode\")({\n device_code: Schema.NonEmptyString,\n user_code: Schema.NonEmptyString,\n verification_uri: Schema.NonEmptyString,\n verification_uri_complete: Schema.OptionFromOptionalKey(Schema.String),\n expires_in: Schema.Int.pipe(\n Schema.withConstructorDefault(\n Effect.succeed(Duration.toSeconds(DEVICE_CODE_LIFETIME)),\n ),\n Schema.withDecodingDefaultKey(\n Effect.succeed(Duration.toSeconds(DEVICE_CODE_LIFETIME)),\n ),\n ),\n interval: Schema.Int.pipe(\n Schema.withConstructorDefault(\n Effect.succeed(Duration.toSeconds(DEVICE_POLL_INTERVAL)),\n ),\n Schema.withDecodingDefaultKey(\n Effect.succeed(Duration.toSeconds(DEVICE_POLL_INTERVAL)),\n ),\n ),\n}) {}\n\n/**\n * What `POST /device/token` answers: a session token, or one RFC 8628 error.\n * better-auth returns the error fields with a 400, so both live in one shape.\n */\nexport class DeviceToken extends Schema.Class<DeviceToken>(\"DeviceToken\")({\n access_token: Schema.OptionFromOptionalKey(Schema.String),\n error: Schema.OptionFromOptionalKey(Schema.String),\n error_description: Schema.OptionFromOptionalKey(Schema.String),\n}) {}\n\n/** One poll's verdict: the session token, or why polling must continue/stop. */\ntype DevicePoll =\n | { readonly _tag: \"Granted\"; readonly token: string }\n | { readonly _tag: \"Pending\" }\n | { readonly _tag: \"SlowDown\" }\n | { readonly _tag: \"Refused\"; readonly failure: CliFailure };\n\nconst retryLine = \"run `braven login --device` again\";\n\nconst deviceExpiredFailure = (): CliFailure =>\n new CliFailure({\n message: `the device code expired before it was approved \u2014 ${retryLine}`,\n exitCode: EXIT_FAILURE,\n });\n\nconst transportFailure = (\n origin: string,\n cause: { readonly message: string },\n): CliFailure =>\n new CliFailure({\n message: `cannot reach the sign-in API at ${origin}: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n });\n\nconst detailOf = (description: Option.Option<string>): string =>\n Option.match(description, {\n onNone: () => \"\",\n onSome: (value) => `: ${oneLine(value)}`,\n });\n\n/** One actionable line per RFC 8628 terminal error. Never the device code. */\nconst refusalFor = (\n error: string,\n description: Option.Option<string>,\n): CliFailure =>\n Match.value(error).pipe(\n Match.when(\"expired_token\", deviceExpiredFailure),\n Match.when(\n \"access_denied\",\n () =>\n new CliFailure({\n message: `the sign-in was denied in the browser \u2014 ${retryLine}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n Match.when(\n \"invalid_grant\",\n () =>\n new CliFailure({\n message: `the device code is no longer valid \u2014 ${retryLine}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n Match.orElse(\n (other) =>\n new CliFailure({\n message: `the device sign-in failed (${other})${detailOf(description)}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n\nconst refusal = (cause: CliFailure): DevicePoll => ({\n _tag: \"Refused\",\n failure: cause,\n});\n\n/** Decodes one token answer into the next poll verdict. */\nconst verdictOf = (body: DeviceToken): Effect.Effect<DevicePoll, CliFailure> =>\n Option.match(body.access_token, {\n onSome: (token) => Effect.succeed<DevicePoll>({ _tag: \"Granted\", token }),\n onNone: () =>\n Match.value(Option.getOrElse(body.error, () => \"unknown_error\")).pipe(\n Match.when(\"authorization_pending\", () =>\n Effect.succeed<DevicePoll>({ _tag: \"Pending\" }),\n ),\n Match.when(\"slow_down\", () =>\n Effect.succeed<DevicePoll>({ _tag: \"SlowDown\" }),\n ),\n Match.orElse((error) =>\n Effect.fail(refusalFor(error, body.error_description)),\n ),\n ),\n });\n\n/**\n * `POST /device/code`: what to show the person, where they approve it, and how\n * long the code lives.\n */\nexport const requestDeviceCode = (\n origin: string,\n clientId: string,\n): Effect.Effect<DeviceCode, CliFailure, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const request = yield* HttpClientRequest.post(\n deviceUrl(origin, \"code\"),\n ).pipe(\n HttpClientRequest.bodyJson({\n client_id: clientId,\n scope: DEVICE_SCOPE,\n }),\n Effect.mapError((cause) => transportFailure(origin, cause)),\n );\n const response = yield* client\n .execute(request)\n .pipe(Effect.mapError((cause) => transportFailure(origin, cause)));\n if (response.status !== 200) {\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"));\n return yield* fail(\n `the sign-in API refused a device code (${response.status})${detail ? `: ${oneLine(detail)}` : \"\"} \u2014 ${retryLine}`,\n );\n }\n return yield* HttpClientResponse.schemaBodyJson(DeviceCode)(response).pipe(\n Effect.mapError(\n (cause) =>\n new CliFailure({\n message: `cannot read a device code from ${origin}: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n });\n\n/** One `POST /device/token`; every answer is a verdict, never a thrown error. */\nconst pollDeviceToken = (\n origin: string,\n clientId: string,\n deviceCode: string,\n): Effect.Effect<DevicePoll, never, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const response = yield* client\n .execute(\n HttpClientRequest.post(deviceUrl(origin, \"token\")).pipe(\n // better-auth's `/device/token` reads JSON only; a form body is 415.\n HttpClientRequest.bodyJsonUnsafe({\n grant_type: DEVICE_GRANT_TYPE,\n device_code: deviceCode,\n client_id: clientId,\n }),\n ),\n )\n .pipe(Effect.mapError((cause) => transportFailure(origin, cause)));\n const body = yield* HttpClientResponse.schemaBodyJson(DeviceToken)(\n response,\n ).pipe(\n Effect.mapError(\n () =>\n new CliFailure({\n message: `the sign-in API at ${origin} returned an unreadable device-token answer`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n return yield* verdictOf(body);\n }).pipe(\n // The poll's contract is a verdict, never a failure: a transport or decode\n // error stops the schedule as `Refused` and surfaces one line later.\n Effect.catch((cause) => Effect.succeed(refusal(cause))),\n );\n\n/**\n * The poll cadence: `interval` seconds, plus five more after every\n * `slow_down`. Grant and refusal stop the schedule; pending keeps it going.\n * Exported so the cadence is provable without a clock.\n */\nexport const devicePollSchedule = (\n start: Duration.Duration,\n): Schedule.Schedule<DevicePoll, DevicePoll> =>\n Schedule.fromStepWithMetadata(\n Effect.map(\n Ref.make(start),\n (interval) => (metadata: Schedule.InputMetadata<DevicePoll>) =>\n Match.value(metadata.input).pipe(\n Match.tag(\"Granted\", () => Cause.done(metadata.input)),\n Match.tag(\"Refused\", () => Cause.done(metadata.input)),\n Match.tag(\"Pending\", () =>\n Ref.get(interval).pipe(\n Effect.map((delay) => Tuple.make(metadata.input, delay)),\n ),\n ),\n Match.tag(\"SlowDown\", () =>\n Ref.updateAndGet(interval, (delay) =>\n Duration.millis(\n Duration.toMillis(delay) + Duration.toMillis(DEVICE_SLOW_DOWN),\n ),\n ).pipe(Effect.map((delay) => Tuple.make(metadata.input, delay))),\n ),\n Match.exhaustive,\n ),\n ),\n );\n\n/**\n * Signs in through better-auth's device authorization grant.\n *\n * Prints and opens the verification URL, polls `/device/token` on the\n * server's interval (adding five seconds per `slow_down`), and returns the\n * session token once approved. The wait is bounded by the code's `expires_in`\n * (30 minutes when the server omits it); expiry, denial and an invalid code\n * each fail with one actionable line.\n */\nexport const deviceLogin = (options: {\n readonly origin: string;\n readonly clientId: string;\n readonly timeout?: Duration.Input | undefined;\n}): Effect.Effect<string, CliFailure, Browser | HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const code = yield* requestDeviceCode(options.origin, options.clientId);\n const verification = Option.getOrElse(\n code.verification_uri_complete,\n () => code.verification_uri,\n );\n // Print as well as open: a headless machine has no browser to open with.\n yield* Console.log(\n `Sign in at ${verification}\\nand confirm the code ${code.user_code} in the browser.`,\n );\n const browser = yield* Browser;\n yield* browser.open(verification);\n const interval = Duration.seconds(code.interval);\n const poll = pollDeviceToken(\n options.origin,\n options.clientId,\n code.device_code,\n );\n // RFC 8628 \u00A73.5: wait `interval` before the first poll, then let the\n // schedule pace every later one \u2014 delaying each repetition would double it.\n const untilFirstPoll = yield* Ref.make(true);\n const outcome = yield* Effect.suspend(() =>\n Effect.flatMap(Ref.getAndSet(untilFirstPoll, false), (isFirst) =>\n isFirst ? Effect.delay(poll, interval) : poll,\n ),\n ).pipe(\n Effect.repeat(devicePollSchedule(interval)),\n Effect.timeoutOrElse({\n duration: options.timeout ?? Duration.seconds(code.expires_in),\n orElse: () => Effect.fail(deviceExpiredFailure()),\n }),\n );\n return yield* Match.value(outcome).pipe(\n Match.tag(\"Granted\", (granted) => Effect.succeed(granted.token)),\n Match.tag(\"Refused\", (refused) => Effect.fail(refused.failure)),\n Match.orElse(() => Effect.fail(deviceExpiredFailure())),\n );\n });\n", "import { Context, Layer, Match, Option } from \"effect\";\nimport * as Str from \"effect/String\";\n\n/** The hosted API origin used when neither the flag nor the environment names one. */\nexport const DEFAULT_API_ORIGIN = \"https://api.bravenlab.com\";\n\n/** The hosted web origin that performs the browser sign-in. */\nexport const DEFAULT_WEB_ORIGIN = \"https://bravenlab.com\";\n\n/** The public client id `braven login --device` presents to better-auth. */\nexport const DEFAULT_DEVICE_CLIENT_ID = \"braven-cli\";\n\n/**\n * The process facts the identity convention depends on.\n *\n * `values` is the process environment (`process.env`) captured at the CLI's\n * boundary; everything below is a pure function of this shape, so tests point\n * the store at a temp directory and a literal environment.\n */\nexport interface EnvironmentShape {\n readonly platform: NodeJS.Platform;\n readonly home: string;\n /** The directory the command was run in; `.braven/` lives here. */\n readonly cwd: string;\n readonly values: Readonly<Record<string, string | undefined>>;\n}\n\n/** The process environment, injected so no command reads `process.env` directly. */\nexport class Environment extends Context.Service<\n Environment,\n EnvironmentShape\n>()(\"braven/Environment\") {}\n\n/** Provides a fixed environment; `bin.ts` supplies the real one. */\nexport const environmentLayer = (\n shape: EnvironmentShape,\n): Layer.Layer<Environment> => Layer.succeed(Environment)(shape);\n\nconst value = (shape: EnvironmentShape, name: string): Option.Option<string> =>\n Option.fromNullishOr(shape.values[name]).pipe(Option.filter(Str.isNonEmpty));\n\nconst stripTrailing = (path: string): string =>\n Str.replace(/[\\\\/]+$/, \"\")(path);\n\n/**\n * The non-secret config file's path \u2014 the convention in\n * `packages/identity/README.md`, mirrored exactly:\n *\n * - macOS: `~/Library/Application Support/braven/config.json` (XDG wins)\n * - Linux: `${XDG_CONFIG_HOME:-~/.config}/braven/config.json`\n * - Windows: `%APPDATA%\\braven\\config.json`\n */\nexport const configPath = (shape: EnvironmentShape): string => {\n const xdg = value(shape, \"XDG_CONFIG_HOME\");\n const base = Match.value(shape.platform).pipe(\n Match.when(\"darwin\", () =>\n Option.getOrElse(xdg, () => `${shape.home}/Library/Application Support`),\n ),\n Match.when(\"win32\", () =>\n Option.getOrElse(\n value(shape, \"APPDATA\"),\n () => `${shape.home}\\\\AppData\\\\Roaming`,\n ),\n ),\n Match.orElse(() => Option.getOrElse(xdg, () => `${shape.home}/.config`)),\n );\n const separator = shape.platform === \"win32\" ? \"\\\\\" : \"/\";\n return `${stripTrailing(base)}${separator}braven${separator}config.json`;\n};\n\n/**\n * `--origin` > `$BRAVEN_API_URL` > the stored default > the hosted API; trailing\n * slashes go. The stored default is what the shell's Settings \u2192 Backend writes,\n * so an agent launched later joins the same server without a flag.\n */\nexport const resolveOrigin = (\n flag: Option.Option<string>,\n shape: EnvironmentShape,\n stored: Option.Option<string> = Option.none(),\n): string =>\n stripTrailing(\n Option.getOrElse(flag, () =>\n Option.getOrElse(value(shape, \"BRAVEN_API_URL\"), () =>\n Option.getOrElse(stored, () => DEFAULT_API_ORIGIN),\n ),\n ),\n );\n\n/** `--web-origin` > `$BRAVEN_WEB_URL` > the stored default > the hosted web app. */\nexport const resolveWebOrigin = (\n flag: Option.Option<string>,\n shape: EnvironmentShape,\n stored: Option.Option<string> = Option.none(),\n): string =>\n stripTrailing(\n Option.getOrElse(flag, () =>\n Option.getOrElse(value(shape, \"BRAVEN_WEB_URL\"), () =>\n Option.getOrElse(stored, () => DEFAULT_WEB_ORIGIN),\n ),\n ),\n );\n\n/** `--client-id` > `$BRAVEN_CLIENT_ID` > `braven-cli`. */\nexport const resolveClientId = (\n flag: Option.Option<string>,\n shape: EnvironmentShape,\n): string =>\n Option.getOrElse(flag, () =>\n Option.getOrElse(\n value(shape, \"BRAVEN_CLIENT_ID\"),\n () => DEFAULT_DEVICE_CLIENT_ID,\n ),\n );\n\n/** `--project-id` > `$BRAVEN_PROJECT_ID`; the store's default is the fallback. */\nexport const resolveProjectFlag = (\n flag: Option.Option<string>,\n shape: EnvironmentShape,\n): Option.Option<string> =>\n Option.isSome(flag) ? flag : value(shape, \"BRAVEN_PROJECT_ID\");\n", "import { Effect } from \"effect\";\nimport * as Schema from \"effect/Schema\";\nimport * as Str from \"effect/String\";\n\n/** The command failed: a rejected sign-in, an unreachable API, an unreadable credentials file. */\nexport const EXIT_FAILURE = 1;\n/** The arguments cannot work. */\nexport const EXIT_USAGE = 2;\n/** Not signed in, or no stored account for the email asked about. */\nexport const EXIT_NOT_SIGNED_IN = 3;\n\n/**\n * One expected failure, carrying the exit code the process boundary uses.\n *\n * Every command fails with this; the boundary flattens it to a single\n * `braven: \u2026` line, so no message can smuggle a newline (or a secret) out.\n */\nexport class CliFailure extends Schema.TaggedError<CliFailure>()(\"CliFailure\", {\n message: Schema.String,\n exitCode: Schema.Int,\n}) {}\n\n/** Fails with a one-line, actionable message and an exit code. */\nexport const fail = (message: string, exitCode: number = EXIT_FAILURE) =>\n Effect.fail(new CliFailure({ message, exitCode }));\n\n/** Collapses any provider text to one line, bounded so a stack dump cannot leak. */\nexport const oneLine = (value: unknown): string =>\n Str.trim(Str.replace(/\\s+/g, \" \")(String(value))).slice(0, 300);\n", "import { Context, Effect, Layer, Option, pipe } from \"effect\";\nimport * as Arr from \"effect/Array\";\nimport * as R from \"effect/Record\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Schema from \"effect/Schema\";\nimport * as Str from \"effect/String\";\nimport { Environment, DEFAULT_API_ORIGIN } from \"./environment.ts\";\nimport {\n CliFailure,\n EXIT_FAILURE,\n EXIT_NOT_SIGNED_IN,\n fail,\n oneLine,\n} from \"./errors.ts\";\n\n/** The config file's schema version. */\nexport const CONFIG_VERSION = 1;\n\nconst CredentialKind = Schema.Literals([\"session\", \"apiKey\"]);\n\n/** The two credential kinds the API accepts. */\nexport type CredentialKind = typeof CredentialKind.Type;\n\n/** One signed-in identity, exactly as recorded in the config file. */\nexport class Account extends Schema.Class<Account>(\"Account\")({\n origin: Schema.String,\n email: Schema.String,\n kind: CredentialKind,\n label: Schema.OptionFromOptionalKey(Schema.String),\n}) {}\n\n/**\n * The version-1 config file: accounts, the current one, the CLI defaults and\n * the watcher's own settings. It never holds a secret.\n */\nexport class ConfigFile extends Schema.Class<ConfigFile>(\"ConfigFile\")({\n version: Schema.Int.pipe(\n Schema.withConstructorDefault(Effect.succeed(CONFIG_VERSION)),\n Schema.withDecodingDefaultKey(Effect.succeed(CONFIG_VERSION)),\n ),\n current: Schema.OptionFromNullishOr(Schema.String, {\n onNoneEncoding: null,\n }).pipe(\n Schema.withConstructorDefault(Effect.succeed(Option.none())),\n Schema.withDecodingDefaultKey(Effect.succeed(null)),\n ),\n accounts: Schema.Array(Account).pipe(\n Schema.withConstructorDefault(Effect.succeed([])),\n Schema.withDecodingDefaultKey(Effect.succeed([])),\n ),\n defaults: Schema.Record(Schema.String, Schema.Unknown).pipe(\n Schema.withConstructorDefault(Effect.succeed({})),\n Schema.withDecodingDefaultKey(Effect.succeed({})),\n ),\n watcher: Schema.Record(Schema.String, Schema.Unknown).pipe(\n Schema.withConstructorDefault(Effect.succeed({})),\n Schema.withDecodingDefaultKey(Effect.succeed({})),\n ),\n}) {}\n\n/** The prefix every API key the server mints carries (`@braven/auth` mints\n * them and cannot import this source, so its literal is the same). */\nexport const API_KEY_PREFIX = \"bvn_api_\";\n\nconst ConfigJson = Schema.fromJsonString(ConfigFile, { space: 2 });\n\n/** Where this process reads and writes the non-secret config file. */\nexport class StoreConfig extends Context.Service<\n StoreConfig,\n { readonly path: string }\n>()(\"braven/StoreConfig\") {}\n\n/**\n * Where secrets live: a `0600` JSON file beside the config file, keyed by the\n * same `{origin}|{email}` account string. opencode's model, deliberately \u2014 an\n * OS keychain buys encryption at rest but charges a permission prompt for\n * every new binary and a native addon in every consumer, while any process\n * running as the user can read either store.\n */\nexport const CREDENTIALS_FILE = \"credentials.json\";\n\n/** The slice of the secret store the identity code uses; tests substitute a map. */\nexport interface SecretsShape {\n readonly get: (\n account: string,\n ) => Effect.Effect<Option.Option<string>, CliFailure>;\n readonly set: (\n account: string,\n secret: string,\n ) => Effect.Effect<void, CliFailure>;\n readonly remove: (account: string) => Effect.Effect<void, CliFailure>;\n}\n\n/** The secret store. */\nexport class Secrets extends Context.Service<Secrets, SecretsShape>()(\n \"braven/Secrets\",\n) {}\n\nconst SecretsJson = Schema.fromJsonString(\n Schema.Record(Schema.String, Schema.String),\n { space: 2 },\n);\n\n/** The credentials file that sits beside a config file. */\nexport const credentialsPath = (configPath: string): string =>\n Str.replace(/config\\.json$/, CREDENTIALS_FILE)(configPath);\n\n/** The real store: the credentials file, mode `0600`, written atomically. */\nexport const secretsLayer: Layer.Layer<\n Secrets,\n never,\n StoreConfig | FileSystem.FileSystem | Path.Path\n> = Layer.effect(Secrets)(\n Effect.gen(function* () {\n const { path } = yield* StoreConfig;\n const fs = yield* FileSystem.FileSystem;\n const pathService = yield* Path.Path;\n const file = credentialsPath(path);\n\n const read = () =>\n Effect.gen(function* () {\n if (\n !(yield* fs\n .exists(file)\n .pipe(Effect.mapError(storageFailure(\"read\", file))))\n ) {\n return {} as Record<string, string>;\n }\n const text = yield* fs\n .readFileString(file)\n .pipe(Effect.mapError(storageFailure(\"read\", file)));\n return yield* Schema.decodeUnknownEffect(SecretsJson)(text).pipe(\n Effect.mapError(\n (cause) =>\n new CliFailure({\n message: `${file} is not a credentials file: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n });\n\n const write = (secrets: Readonly<Record<string, string>>) =>\n Effect.gen(function* () {\n const text = yield* Schema.encodeUnknownEffect(SecretsJson)(\n secrets,\n ).pipe(Effect.mapError(storageFailure(\"encode\", file)));\n yield* fs\n .makeDirectory(pathService.dirname(file), { recursive: true })\n .pipe(Effect.mapError(storageFailure(\"create\", file)));\n const temporary = `${file}.tmp`;\n yield* fs\n .writeFileString(temporary, text, { mode: 0o600 })\n .pipe(Effect.mapError(storageFailure(\"write\", file)));\n yield* fs\n .chmod(temporary, 0o600)\n .pipe(Effect.mapError(storageFailure(\"write\", file)));\n yield* fs\n .rename(temporary, file)\n .pipe(Effect.mapError(storageFailure(\"write\", file)));\n });\n\n const update = (\n change: (secrets: Record<string, string>) => Record<string, string>,\n ) =>\n Effect.gen(function* () {\n yield* write(change(yield* read()));\n });\n\n return Secrets.of({\n get: (account) =>\n read().pipe(Effect.map((all) => Option.fromNullishOr(all[account]))),\n set: (account, secret) => update((all) => R.set(all, account, secret)),\n remove: (account) => update((all) => R.remove(all, account)),\n });\n }),\n);\n\n/** The config `current` value and the credentials key for an identity. */\nexport const accountKey = (origin: string, email: string): string =>\n `${normalizeOrigin(origin)}|${email}`;\n\nconst normalizeOrigin = (origin: string): string =>\n Str.replace(/\\/+$/, \"\")(origin);\n\nconst storageFailure =\n (action: string, path: string) =>\n (cause: { readonly message: string }): CliFailure =>\n new CliFailure({\n message: `${action} ${path}: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n });\n\n/** Reads the config file; a missing file is an empty config, never an error. */\nexport const loadConfig = (): Effect.Effect<\n ConfigFile,\n CliFailure,\n StoreConfig | FileSystem.FileSystem\n> =>\n Effect.gen(function* () {\n const { path } = yield* StoreConfig;\n const fs = yield* FileSystem.FileSystem;\n if (\n !(yield* fs\n .exists(path)\n .pipe(Effect.mapError(storageFailure(\"read\", path))))\n ) {\n return new ConfigFile({});\n }\n const text = yield* fs\n .readFileString(path)\n .pipe(Effect.mapError(storageFailure(\"read\", path)));\n const config = yield* Schema.decodeUnknownEffect(ConfigJson)(text).pipe(\n Effect.mapError(\n (cause) =>\n new CliFailure({\n message: `${path} is not a version-${CONFIG_VERSION} config: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n if (config.version !== CONFIG_VERSION) {\n return yield* fail(\n `${path} is version ${config.version}, expected ${CONFIG_VERSION}`,\n );\n }\n return config;\n });\n\n/** Writes the config file atomically, creating its directory if needed. */\nexport const saveConfig = (\n config: ConfigFile,\n): Effect.Effect<\n void,\n CliFailure,\n StoreConfig | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const { path } = yield* StoreConfig;\n const fs = yield* FileSystem.FileSystem;\n const pathService = yield* Path.Path;\n const text = yield* Schema.encodeUnknownEffect(ConfigJson)(config).pipe(\n Effect.mapError(storageFailure(\"encode\", path)),\n );\n yield* fs\n .makeDirectory(pathService.dirname(path), { recursive: true })\n .pipe(Effect.mapError(storageFailure(\"create\", path)));\n const temporary = `${path}.tmp`;\n yield* fs\n .writeFileString(temporary, text)\n .pipe(Effect.mapError(storageFailure(\"write\", path)));\n yield* fs\n .rename(temporary, path)\n .pipe(Effect.mapError(storageFailure(\"write\", path)));\n });\n\nconst matches =\n (origin: Option.Option<string>, email: Option.Option<string>) =>\n (account: Account): boolean =>\n Option.match(origin, {\n onNone: () => true,\n onSome: (value) =>\n normalizeOrigin(account.origin) === normalizeOrigin(value),\n }) &&\n Option.match(email, {\n onNone: () => true,\n onSome: (value) => account.email === value,\n });\n\n/** The first stored account matching the given origin and/or email. */\nexport const findAccount = (\n config: ConfigFile,\n origin: Option.Option<string>,\n email: Option.Option<string>,\n): Option.Option<Account> =>\n pipe(config.accounts, Arr.findFirst(matches(origin, email)));\n\n/** The account the config points at, if any. */\nexport const currentAccount = (config: ConfigFile): Option.Option<Account> =>\n Option.flatMap(config.current, (key) =>\n pipe(\n config.accounts,\n Arr.findFirst(\n (account) => accountKey(account.origin, account.email) === key,\n ),\n ),\n );\n\n/** A secret to store, with the owner the API proved (or `--email` asserted). */\nexport interface CredentialInput {\n readonly origin: string;\n readonly email: string;\n readonly kind: CredentialKind;\n readonly secret: string;\n}\n\n/**\n * Writes a secret to the credentials file, records its account, and makes it\n * current. The secret lands first; a failing write aborts before the config\n * file changes, so a stored account always has its secret.\n */\nexport const storeCredential = (\n credential: CredentialInput,\n label: Option.Option<string> = Option.none(),\n): Effect.Effect<\n Account,\n CliFailure,\n StoreConfig | Secrets | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n if (credential.secret.length === 0) {\n return yield* fail(\"refusing to store an empty secret\");\n }\n if (credential.email.length === 0) {\n return yield* fail(\"storing a credential needs an email\");\n }\n const origin = normalizeOrigin(credential.origin);\n const key = accountKey(origin, credential.email);\n const secrets = yield* Secrets;\n yield* secrets.set(key, credential.secret);\n const config = yield* loadConfig();\n const account = new Account({\n origin,\n email: credential.email,\n kind: credential.kind,\n label,\n });\n yield* saveConfig(\n new ConfigFile({\n ...config,\n accounts: [\n ...config.accounts.filter(\n (stored) => accountKey(stored.origin, stored.email) !== key,\n ),\n account,\n ],\n current: Option.some(key),\n }),\n );\n return account;\n });\n\n/** Points `current` at an already-stored account. */\nexport const useAccount = (\n email: string,\n): Effect.Effect<\n Account,\n CliFailure,\n StoreConfig | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n const account = findAccount(config, Option.none(), Option.some(email));\n if (Option.isNone(account)) {\n return yield* fail(\n `no stored account for ${email} \u2014 see \\`braven config\\``,\n EXIT_NOT_SIGNED_IN,\n );\n }\n yield* saveConfig(\n new ConfigFile({\n ...config,\n current: Option.some(\n accountKey(account.value.origin, account.value.email),\n ),\n }),\n );\n return account.value;\n });\n\n/**\n * Forgets an account (default: the current one), its secret included.\n *\n * When the removed account was current, `current` rotates to the first\n * remaining account.\n */\nexport const removeCredential = (\n email: Option.Option<string>,\n): Effect.Effect<\n Option.Option<Account>,\n CliFailure,\n StoreConfig | Secrets | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n const account = Option.isSome(email)\n ? findAccount(config, Option.none(), email)\n : currentAccount(config);\n if (Option.isNone(account)) return Option.none();\n const target = account.value;\n const key = accountKey(target.origin, target.email);\n const secrets = yield* Secrets;\n yield* secrets.remove(key);\n const accounts = config.accounts.filter(\n (stored) => accountKey(stored.origin, stored.email) !== key,\n );\n const next = Arr.head(accounts).pipe(\n Option.map((stored) => accountKey(stored.origin, stored.email)),\n );\n yield* saveConfig(\n new ConfigFile({\n ...config,\n accounts,\n current: Option.contains(config.current, key) ? next : config.current,\n }),\n );\n return Option.some(target);\n });\n\n/** Resolves the default company/project, either of which may be unset. */\nexport const resolveDefaults = (): Effect.Effect<\n {\n readonly company: Option.Option<string>;\n readonly project: Option.Option<string>;\n },\n CliFailure,\n StoreConfig | FileSystem.FileSystem\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n const defaultOf = (key: string): Option.Option<string> =>\n Option.fromNullishOr(config.defaults[key]).pipe(\n Option.filter(Predicate.isString),\n );\n return { company: defaultOf(\"company\"), project: defaultOf(\"project\") };\n });\n\n/** A stored non-secret default (\"origin\", \"company\", \"project\"). */\nexport const readDefault = (\n key: string,\n): Effect.Effect<\n Option.Option<string>,\n CliFailure,\n StoreConfig | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n return Option.fromNullishOr(config.defaults[key]).pipe(\n Option.filter(Predicate.isString),\n );\n });\n\n/** Writes (or, with `None`, removes) one non-secret default. */\nexport const setDefault = (\n name: string,\n value: Option.Option<string>,\n): Effect.Effect<\n ConfigFile,\n CliFailure,\n StoreConfig | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n const updated = new ConfigFile({\n ...config,\n defaults: Option.match(value, {\n onNone: () => R.remove(config.defaults, name),\n onSome: (next) => R.set(config.defaults, name, next.trim()),\n }),\n });\n yield* saveConfig(updated);\n return updated;\n });\n\n/** A resolved secret and where it came from. Never printed. */\nexport interface Credential {\n readonly origin: string;\n readonly kind: CredentialKind;\n readonly secret: string;\n readonly email: Option.Option<string>;\n readonly label: Option.Option<string>;\n}\n\nconst envValue = (\n shape: { readonly values: Readonly<Record<string, string | undefined>> },\n name: string,\n): Option.Option<string> =>\n Option.fromNullishOr(shape.values[name]).pipe(Option.filter(Str.isNonEmpty));\n\n/** The stored account's secret, ignoring env overrides. `logout` uses this so a\n * shell's `BRAVEN_SESSION_TOKEN` can never be the credential it revokes. */\nexport const storedCredential = (options: {\n readonly origin: Option.Option<string>;\n readonly email: Option.Option<string>;\n}): Effect.Effect<\n Option.Option<Credential>,\n CliFailure,\n StoreConfig | Secrets | FileSystem.FileSystem\n> =>\n Effect.gen(function* () {\n const config = yield* loadConfig();\n const account =\n Option.isSome(options.origin) || Option.isSome(options.email)\n ? findAccount(config, options.origin, options.email)\n : currentAccount(config);\n if (Option.isNone(account)) return Option.none();\n const stored = account.value;\n const secrets = yield* Secrets;\n const secret = yield* secrets.get(accountKey(stored.origin, stored.email));\n if (Option.isNone(secret)) return Option.none();\n return Option.some({\n origin: stored.origin,\n kind: stored.kind,\n secret: secret.value,\n email: Option.some(stored.email),\n label: stored.label,\n });\n });\n\n/**\n * Resolves the credential to authenticate with: `env var > flag > store`.\n *\n * `BRAVEN_SESSION_TOKEN` / `BRAVEN_API_KEY` (with `BRAVEN_API_URL`) win and\n * touch neither the config file nor the credentials file.\n */\nexport const resolveCredential = (options: {\n readonly origin: Option.Option<string>;\n readonly email: Option.Option<string>;\n}): Effect.Effect<\n Option.Option<Credential>,\n CliFailure,\n Environment | StoreConfig | Secrets | FileSystem.FileSystem\n> =>\n Effect.gen(function* () {\n const shape = yield* Environment;\n const configuredOrigin = Option.isSome(options.origin)\n ? options.origin\n : envValue(shape, \"BRAVEN_API_URL\");\n const envOrigin = Option.getOrElse(\n configuredOrigin,\n () => DEFAULT_API_ORIGIN,\n );\n const apiKey = envValue(shape, \"BRAVEN_API_KEY\");\n if (Option.isSome(apiKey)) {\n return Option.some({\n origin: normalizeOrigin(envOrigin),\n kind: \"apiKey\",\n secret: apiKey.value,\n email: Option.none(),\n label: Option.some(\"env\"),\n });\n }\n const session = envValue(shape, \"BRAVEN_SESSION_TOKEN\");\n if (Option.isSome(session)) {\n return Option.some({\n origin: normalizeOrigin(envOrigin),\n kind: \"session\",\n secret: session.value,\n email: Option.none(),\n label: Option.some(\"env\"),\n });\n }\n return yield* storedCredential(options);\n });\n", "/**\n * The loopback browser sign-in every surface shares.\n *\n * The flow is one handshake: bind `127.0.0.1:0`, open\n * `{webOrigin}/cli-login?port=<port>` in the system browser, receive the session\n * token on `GET /?token=\u2026`, prove the token against the API, and store it in\n * the file-backed identity store. The CLI and the watcher call the same\n * `signIn`, so a credential has exactly one way in.\n *\n * The listener is owned by `signIn`'s scope: it closes on success, on timeout,\n * on a browser failure, and on cancellation. No step logs the token.\n */\nimport open from \"open\";\nimport { createServer } from \"node:http\";\nimport { NodeHttpServer } from \"@effect/platform-node\";\nimport {\n Console,\n Context,\n Deferred,\n Duration,\n Effect,\n Layer,\n Option,\n Predicate,\n Schema,\n} from \"effect\";\nimport {\n HttpClient,\n HttpClientRequest,\n HttpClientResponse,\n HttpRouter,\n HttpServer,\n HttpServerRequest,\n HttpServerResponse,\n} from \"effect/http\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as NetAddress from \"effect/net/NetAddress\";\nimport * as Path from \"effect/Path\";\nimport * as Str from \"effect/String\";\nimport { CliFailure, EXIT_FAILURE, fail, oneLine } from \"./errors.ts\";\nimport {\n Secrets,\n StoreConfig,\n storeCredential,\n type Account,\n} from \"./identity.ts\";\n\nexport type { Account } from \"./identity.ts\";\n\n/** How long a person has to finish signing in in the browser. */\nexport const LOGIN_TIMEOUT = Duration.minutes(5);\n\n/**\n * Why a browser sign-in did not produce a credential. The CLI flattens this to\n * `CliFailure`; the watcher branches on `reason` to show its own next step.\n */\nexport const SignInReason = Schema.Literals([\n \"timeout\",\n \"listener\",\n \"browser\",\n \"rejected\",\n \"storage\",\n]);\n\nexport type SignInReason = typeof SignInReason.Type;\n\n/** A typed sign-in failure: the listener, the browser, the API or the store. */\nexport class SignInFailure extends Schema.TaggedError<SignInFailure>()(\n \"SignInFailure\",\n { reason: SignInReason, message: Schema.String },\n) {}\n\n/** Opens the system browser; injected so tests never launch a real one. */\nexport class Browser extends Context.Service<\n Browser,\n { readonly open: (url: string) => Effect.Effect<void, CliFailure> }\n>()(\"braven/Browser\") {}\n\n/** The system browser for `braven login` and the watcher's Sign in. */\nexport const browserLayer: Layer.Layer<Browser> = Layer.succeed(Browser)({\n open: (url) =>\n Effect.tryPromise({\n try: () => open(url),\n catch: (cause) =>\n new CliFailure({\n message: `cannot open a browser for ${url}: ${oneLine(cause)}`,\n exitCode: EXIT_FAILURE,\n }),\n }).pipe(Effect.asVoid),\n});\n\n/** The better-auth session lookup: token in, the API's own email out. */\nexport class Sessions extends Context.Service<\n Sessions,\n {\n readonly emailFor: (\n origin: string,\n token: string,\n ) => Effect.Effect<string, CliFailure>;\n /** Revoke a session server-side so a forgotten token cannot be replayed. */\n readonly revoke: (\n origin: string,\n token: string,\n ) => Effect.Effect<void, CliFailure>;\n }\n>()(\"braven/Sessions\") {}\n\nconst SessionUser = class SessionUser extends Schema.Class<SessionUser>(\n \"SessionUser\",\n)({ email: Schema.NonEmptyString }) {};\n\nconst SessionBody = class SessionBody extends Schema.Class<SessionBody>(\n \"SessionBody\",\n)({ user: SessionUser }) {};\n\nconst lookupFailure = (origin: string, cause: { readonly message: string }) =>\n new CliFailure({\n message: `cannot read a session from ${origin}: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n });\n\n/**\n * Asks `GET /api/auth/get-session` who a bearer token belongs to.\n *\n * A non-200, a non-JSON body, or a body without `user.email` fails loudly \u2014\n * `login` stores nothing unless this proves the owner.\n */\nexport const lookupEmail = (\n origin: string,\n token: string,\n): Effect.Effect<string, CliFailure, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const response = yield* client\n .get(`${origin}/api/auth/get-session`, {\n headers: { authorization: `Bearer ${token}` },\n })\n .pipe(Effect.mapError((cause) => lookupFailure(origin, cause)));\n if (response.status !== 200) {\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"));\n return yield* fail(\n `the API rejected that sign-in (${response.status})${detail ? `: ${oneLine(detail)}` : \"\"} \u2014 run \\`braven login\\` again`,\n );\n }\n const body = yield* HttpClientResponse.schemaBodyJson(SessionBody)(\n response,\n ).pipe(Effect.mapError((cause) => lookupFailure(origin, cause)));\n return body.user.email;\n });\n\n/** Revokes a session (`POST /api/auth/sign-out`). A 200 or a 401 both mean the\n * token is dead \u2014 already-gone is not a failure. */\nexport const revokeSession = (\n origin: string,\n token: string,\n): Effect.Effect<void, CliFailure, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const response = yield* client\n .execute(\n HttpClientRequest.post(`${origin}/api/auth/sign-out`).pipe(\n HttpClientRequest.bearerToken(token),\n HttpClientRequest.bodyJsonUnsafe({}),\n ),\n )\n .pipe(\n Effect.mapError(\n (cause) =>\n new CliFailure({\n message: `cannot reach ${origin} to sign out: ${oneLine(cause.message)}`,\n exitCode: EXIT_FAILURE,\n }),\n ),\n );\n if (response.status === 200 || response.status === 401) return;\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"));\n return yield* fail(\n `the API refused the sign-out (${response.status})${detail ? `: ${oneLine(detail)}` : \"\"}`,\n );\n });\n\n/** Live sessions over the platform HTTP client. */\nexport const sessionsLayer: Layer.Layer<\n Sessions,\n never,\n HttpClient.HttpClient\n> = Layer.effect(Sessions)(\n Effect.map(HttpClient.HttpClient, (client) => ({\n emailFor: (origin, token) =>\n lookupEmail(origin, token).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ),\n revoke: (origin, token) =>\n revokeSession(origin, token).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ),\n })),\n);\n\n/** A loopback token, and the port it arrived on. */\nexport interface LoopbackToken {\n readonly token: string;\n readonly port: number;\n}\n\nconst tokenOf = (\n params: Readonly<Record<string, string | ReadonlyArray<string>>>,\n): Option.Option<string> =>\n Option.fromNullishOr(params[\"token\"]).pipe(\n Option.flatMap((value) =>\n Predicate.isString(value) ? Option.some(value) : Option.none(),\n ),\n Option.filter(Str.isNonEmpty),\n );\n\nconst tokenRoute = (tokens: Deferred.Deferred<string>) =>\n HttpRouter.add(\n \"GET\",\n \"/\",\n Effect.gen(function* () {\n const params = yield* HttpServerRequest.ParsedSearchParams;\n return yield* Option.match(tokenOf(params), {\n onNone: () =>\n Effect.succeed(\n HttpServerResponse.text(\"Missing token.\", { status: 400 }),\n ),\n onSome: (token) =>\n Effect.gen(function* () {\n yield* Deferred.succeed(tokens, token);\n return HttpServerResponse.html(\n \"<h1>Authenticated. You can close this window.</h1>\",\n );\n }),\n });\n }),\n );\n\nconst listenerFailure = (message: string): SignInFailure =>\n new SignInFailure({ reason: \"listener\", message });\n\nconst portOf = (\n address: NetAddress.SocketAddress,\n): Effect.Effect<number, SignInFailure> =>\n NetAddress.isInetAddress(address)\n ? Effect.succeed(address.port)\n : Effect.fail(\n listenerFailure(\"the login listener did not bind a TCP port\"),\n );\n\n/** Where `signIn` reports the URL it opened, before it waits for the token. */\nexport type LoginUrlListener = (url: string) => Effect.Effect<void>;\n\nconst openingLines = (url: string): string =>\n `Opening ${url}\\nIf your browser did not open, visit that URL to finish signing in.`;\n\n/**\n * Binds `127.0.0.1:0`, opens `{webOrigin}/cli-login?port=<port>`, and waits for\n * the browser to hand the session token back to `GET /?token=\u2026`.\n *\n * `onLoginUrl` receives the URL once it is known; the CLI omits it and gets the\n * two printed lines, the watcher passes its own progress sink. The listener is\n * owned by the returned effect's scope: it closes on success, on the\n * five-minute timeout, on a browser failure, and on interruption.\n */\nexport const awaitToken = (options: {\n readonly webOrigin: string;\n readonly timeout?: Duration.Input | undefined;\n readonly onLoginUrl?: LoginUrlListener | undefined;\n}): Effect.Effect<LoopbackToken, SignInFailure, Browser> =>\n Effect.gen(function* () {\n const tokens = yield* Deferred.make<string>();\n const app = HttpRouter.serve(\n Layer.mergeAll(\n tokenRoute(tokens),\n HttpRouter.add(\n \"*\",\n \"*\",\n HttpServerResponse.text(\"Missing token.\", { status: 400 }),\n ),\n ),\n {\n disableLogger: true,\n disableListenLog: true,\n },\n ).pipe(\n Layer.provideMerge(\n NodeHttpServer.layer(createServer, { host: \"127.0.0.1\", port: 0 }),\n ),\n );\n return yield* Effect.gen(function* () {\n const server = yield* HttpServer.HttpServer;\n const port = yield* portOf(server.address);\n const browser = yield* Browser;\n const loginUrl = `${options.webOrigin}/cli-login?port=${port}`;\n // Print it as well as opening it: `open` can fail silently on a headless\n // machine or a locked-down desktop, and then the URL is the only way in.\n yield* Option.match(Option.fromNullishOr(options.onLoginUrl), {\n onNone: () => Console.log(openingLines(loginUrl)),\n onSome: (report) => report(loginUrl),\n });\n yield* browser.open(loginUrl).pipe(\n Effect.mapError(\n (failure) =>\n new SignInFailure({\n reason: \"browser\",\n message: failure.message,\n }),\n ),\n );\n return { token: yield* Deferred.await(tokens), port };\n }).pipe(\n Effect.timeoutOrElse({\n duration: options.timeout ?? LOGIN_TIMEOUT,\n orElse: () =>\n Effect.fail(\n new SignInFailure({\n reason: \"timeout\",\n message:\n \"timed out waiting for the browser sign-in (5 minutes) \u2014 run `braven login` again\",\n }),\n ),\n }),\n Effect.provide(app),\n Effect.catchTag(\"ServeError\", (cause) =>\n Effect.fail(\n listenerFailure(\n `cannot start the login listener: ${oneLine(cause.message)}`,\n ),\n ),\n ),\n );\n });\n\nconst asSignInFailure =\n (reason: \"rejected\" | \"storage\") =>\n (failure: { readonly message: string }): SignInFailure =>\n new SignInFailure({ reason, message: failure.message });\n\n/**\n * Proves a session token's owner and stores it, without printing anything.\n *\n * Nothing is written until `GET /api/auth/get-session` confirms the token\n * belongs to an email; a rejection or an unreachable API leaves the store\n * untouched.\n */\nexport const rememberSession = (\n origin: string,\n token: string,\n): Effect.Effect<\n Account,\n SignInFailure,\n Sessions | StoreConfig | Secrets | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const sessions = yield* Sessions;\n const email = yield* sessions\n .emailFor(origin, token)\n .pipe(Effect.mapError(asSignInFailure(\"rejected\")));\n return yield* storeCredential({\n origin,\n email,\n kind: \"session\",\n secret: token,\n }).pipe(Effect.mapError(asSignInFailure(\"storage\")));\n });\n\n/**\n * The whole browser sign-in: loopback handshake, prove the token, store it.\n *\n * Returns the stored account (never the secret). Callers own presentation:\n * the CLI prints the identity, the watcher reconnects its sync session.\n */\nexport const signIn = (options: {\n readonly origin: string;\n readonly webOrigin: string;\n readonly timeout?: Duration.Input | undefined;\n readonly onLoginUrl?: LoginUrlListener | undefined;\n}): Effect.Effect<\n Account,\n SignInFailure,\n Browser | Sessions | StoreConfig | Secrets | FileSystem.FileSystem | Path.Path\n> =>\n Effect.gen(function* () {\n const { token } = yield* awaitToken(options);\n return yield* rememberSession(options.origin, token);\n });\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Console", "Duration", "Effect", "Match", "Option", "HttpClient", "HttpClientRequest", "HttpClientResponse", "Schema", "Str", "Context", "Effect", "Layer", "Option", "Schema", "Str", "Effect", "Option", "Context", "Layer", "Effect", "Option", "Option", "Effect", "Context", "Effect", "Layer", "Option", "Predicate", "Schema", "Str", "Schema", "Context", "Layer", "Effect", "SessionUser", "SessionBody", "Option", "Predicate", "Duration", "Effect", "Option", "Match", "HttpClient", "HttpClientRequest", "HttpClientResponse", "Console"]
|
|
7
|
+
}
|