bravenlab 0.1.0 → 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 CHANGED
@@ -4,7 +4,7 @@ The `braven` command: **sign in once per machine** and let the CLI and the
4
4
  Python SDK use the same identity.
5
5
 
6
6
  This package owns no credential convention. The config file's path, its
7
- version-1 schema and the OS keychain naming belong to
7
+ version-1 schema and the account naming belong to
8
8
  [`braven-identity`](../identity/README.md) (`packages/identity`), the contract
9
9
  every surface mirrors — so a `braven login` here is the same login
10
10
  `braven.login()` sees. The TypeScript implementation lives in
@@ -12,7 +12,7 @@ every surface mirrors — so a `braven login` here is the same login
12
12
  `braven-agent` share; the Python package stays the reference.
13
13
 
14
14
  Nothing in this package ever prints, logs or writes a secret: secrets live in
15
- the OS keychain and are handed only to the store.
15
+ the `0600` credentials file and are handed only to the store.
16
16
 
17
17
  ## Install
18
18
 
@@ -67,8 +67,8 @@ node --experimental-strip-types src/bin.ts --help
67
67
 
68
68
  | Command | What it does |
69
69
  | --- | --- |
70
- | `braven login [--origin URL] [--web-origin URL]` | Opens the browser, waits for the signed-in session token on a loopback listener, proves it with `GET {origin}/api/auth/get-session`, stores it in the keychain as `kind="session"`, makes the account current, then prints whoami. **This is the whole sign-in: no password is typed into the terminal.** |
71
- | `braven login --watcher` | Uses the sign-in the Braven app already made on this machine: the app (and the agent it runs) and the CLI share one store, so this only adopts the account and makes it current — no browser, no keychain prompt. Refuses when the app never signed in to the resolved server. |
70
+ | `braven login [--origin URL] [--web-origin URL]` | Opens the browser, waits for the signed-in session token on a loopback listener, proves it with `GET {origin}/api/auth/get-session`, stores it as `kind="session"`, makes the account current, then prints whoami. **This is the whole sign-in: no password is typed into the terminal.** |
71
+ | `braven login --watcher` | Uses the sign-in the Braven app already made on this machine: the app (and the agent it runs) and the CLI share one store, so this only adopts the account and makes it current — no browser, no prompt. Refuses when the app never signed in to the resolved server. |
72
72
  | `braven login --api [--key bvn_api_…] [--email EMAIL]` | Stores a long-lived API key as `kind="apiKey"`; the key comes from `--key` or `$BRAVEN_API_KEY`. For CI, scripts and machines that cannot open a browser. |
73
73
  | `braven login --device [--client-id ID]` | Better-auth's device authorization grant: prints a user code and verification URL, opens it, polls `/api/auth/device/token` on the server's interval, then proves and stores the session token exactly as the loopback flow does. For headless machines and machines without a reliable loopback callback. |
74
74
  | `braven push <file> [--override] [--path PATH] [--project-id ID]` | Uploads one local file through `POST {origin}/files/ingest` with `X-Project-Id` and the stored credential as a bearer. `--path` defaults to the file's basename; `--override` reuses that path's Experiment/File instead of deduping or adding a sibling. |
@@ -77,7 +77,7 @@ node --experimental-strip-types src/bin.ts --help
77
77
  | `braven config` / `braven config show` | The stored accounts, which one is `current`, and the resolved `company` / `project` defaults. |
78
78
  | `braven config set company\|project <value>` | Writes one default. |
79
79
  | `braven config use <email>` | Switches `current`, wherever that account's origin is. |
80
- | `braven logout [--email EMAIL]` | Forgets that account — keychain secret included (default: the current one), rotating `current` to a survivor. A stored session is revoked server-side; a stored API key is only forgotten. |
80
+ | `braven logout [--email EMAIL]` | Forgets that account — stored secret included (default: the current one), rotating `current` to a survivor. A stored session is revoked server-side; a stored API key is only forgotten. |
81
81
  | `braven agent [agent flags]` | Runs the headless watch daemon (`braven-agent`) with the arguments handed over verbatim. Same program, same flags — `braven agent --help` prints the daemon's usage. |
82
82
 
83
83
  `--origin` beats `$BRAVEN_API_URL`, which beats the hosted
@@ -165,7 +165,7 @@ revoke it. Only a stored `session` is revoked — `POST
165
165
  mean it is dead. A `kind="apiKey"` account is only forgotten locally and stays
166
166
  valid until it is revoked under **Settings → API keys**.
167
167
 
168
- The keychain secret and config row then go, and `current` rotates to a survivor
168
+ The stored secret and config row then go, and `current` rotates to a survivor
169
169
  (or none). Because the local account is already gone when revocation runs, an
170
170
  unreachable API is a warning — "the session stays valid until it expires" — not
171
171
  a failure; there is nothing left to retry. With no stored account the command
@@ -194,22 +194,21 @@ Config (never a secret), at the OS convention:
194
194
  ```
195
195
 
196
196
  Writes are atomic (`config.json.tmp` + rename) and preserve `watcher` and the
197
- other defaults. Secret, in the OS keychain via `@napi-rs/keyring` (macOS
198
- Keychain, Windows Credential Manager, libsecret): service `braven`, account
199
- `{origin}|{email}`. **No plaintext fallback**; a refused keychain surfaces as
200
- one line and nothing is written.
197
+ other defaults. The secret lives in `credentials.json` beside it, keyed by
198
+ `{origin}|{email}`, written atomically with mode `0600` — no OS keychain, so a
199
+ freshly installed binary never prompts. A file that cannot be read or written
200
+ surfaces as one line and nothing is stored.
201
201
 
202
202
  Precedence for the secret is `env var > store`: `BRAVEN_SESSION_TOKEN` or
203
- `BRAVEN_API_KEY` (with `BRAVEN_API_URL`) win and touch neither file nor
204
- keychain. The origin is `flag > $BRAVEN_API_URL > hosted`; `--email` selects a
205
- stored account.
203
+ `BRAVEN_API_KEY` (with `BRAVEN_API_URL`) win and touch neither file. The origin
204
+ is `flag > $BRAVEN_API_URL > hosted`; `--email` selects a stored account.
206
205
 
207
206
  ## Exit codes
208
207
 
209
208
  | Code | Meaning |
210
209
  | --- | --- |
211
210
  | `0` | It worked. Help and `--version` too. |
212
- | `1` | The command failed: a rejected session, an unreachable API, a keychain that refused, a corrupt config file. |
211
+ | `1` | The command failed: a rejected session, an unreachable API, an unreadable credentials file, a corrupt config file. |
213
212
  | `2` | The arguments cannot work: an unknown flag, a missing one, a key without the `bvn_api_` prefix, a key whose owner the probe could not prove. |
214
213
  | `3` | Not signed in, or no stored account for the email asked about — so a script can `braven login` and retry. |
215
214
 
@@ -228,12 +227,12 @@ src/
228
227
  # @braven/identity-store/testing)
229
228
  ```
230
229
 
231
- The config file, its schema, the keychain (`Keychain`, `StoreConfig`,
230
+ The config file, its schema, the credentials file (`Secrets`, `StoreConfig`,
232
231
  `Environment`) and the `CliFailure` vocabulary live in
233
232
  [`@braven/identity-store`](../identity-store/README.md), so the CLI and
234
233
  `braven-agent` read one store instead of depending on each other. The seams the
235
234
  CLI adds are `Browser`, `Sessions` and `HttpClient`. Tests replace all of them,
236
- so the suite touches no real keychain, no real browser and no network except
235
+ so the suite touches no real credentials file, no real browser and no network except
237
236
  the loopback listener the sign-in test deliberately starts.
238
237
 
239
238
  `braven agent` is not an `effect/cli` command: `bin.ts` checks `argv[0]` first
package/dist/auth.js CHANGED
@@ -33,7 +33,6 @@ var fail = (message, exitCode = EXIT_FAILURE) => Effect.fail(new CliFailure({ me
33
33
  var oneLine = (value) => Str2.trim(Str2.replace(/\s+/g, " ")(String(value))).slice(0, 300);
34
34
 
35
35
  // ../identity-store/src/identity.ts
36
- import { Entry } from "@napi-rs/keyring";
37
36
  import { Context as Context2, Effect as Effect2, Layer as Layer2, Option as Option2, pipe } from "effect";
38
37
  import * as Arr from "effect/Array";
39
38
  import * as R from "effect/Record";
@@ -43,7 +42,6 @@ import * as Predicate from "effect/Predicate";
43
42
  import * as Schema2 from "effect/Schema";
44
43
  import * as Str3 from "effect/String";
45
44
  var CONFIG_VERSION = 1;
46
- var KEYCHAIN_SERVICE = "braven";
47
45
  var CredentialKind = Schema2.Literals(["session", "apiKey"]);
48
46
  var Account = class extends Schema2.Class("Account")({
49
47
  origin: Schema2.String,
@@ -80,29 +78,56 @@ var ConfigFile = class extends Schema2.Class("ConfigFile")({
80
78
  var ConfigJson = Schema2.fromJsonString(ConfigFile, { space: 2 });
81
79
  var StoreConfig = class extends Context2.Service()("braven/StoreConfig") {
82
80
  };
83
- var Keychain = class extends Context2.Service()(
84
- "braven/Keychain"
81
+ var CREDENTIALS_FILE = "credentials.json";
82
+ var Secrets = class extends Context2.Service()(
83
+ "braven/Secrets"
85
84
  ) {
86
85
  };
87
- var entry = (account) => new Entry(KEYCHAIN_SERVICE, account);
88
- var keychainAction = (action, run) => Effect2.try({
89
- try: run,
90
- catch: (cause) => new CliFailure({
91
- message: `keychain ${action} failed: ${oneLine(cause)}`,
92
- exitCode: EXIT_FAILURE
93
- })
94
- });
95
- var keychainLayer = Layer2.succeed(Keychain)({
96
- get: (account) => keychainAction("read", () => entry(account).getPassword()).pipe(
97
- Effect2.map(Option2.fromNullishOr)
98
- ),
99
- set: (account, secret) => keychainAction("write", () => {
100
- entry(account).setPassword(secret);
101
- }),
102
- remove: (account) => keychainAction("delete", () => {
103
- entry(account).deletePassword();
86
+ var SecretsJson = Schema2.fromJsonString(
87
+ Schema2.Record(Schema2.String, Schema2.String),
88
+ { space: 2 }
89
+ );
90
+ var credentialsPath = (configPath) => Str3.replace(/config\.json$/, CREDENTIALS_FILE)(configPath);
91
+ var secretsLayer = Layer2.effect(Secrets)(
92
+ Effect2.gen(function* () {
93
+ const { path } = yield* StoreConfig;
94
+ const fs = yield* FileSystem.FileSystem;
95
+ const pathService = yield* Path.Path;
96
+ const file = credentialsPath(path);
97
+ const read = () => Effect2.gen(function* () {
98
+ if (!(yield* fs.exists(file).pipe(Effect2.mapError(storageFailure("read", file))))) {
99
+ return {};
100
+ }
101
+ const text = yield* fs.readFileString(file).pipe(Effect2.mapError(storageFailure("read", file)));
102
+ return yield* Schema2.decodeUnknownEffect(SecretsJson)(text).pipe(
103
+ Effect2.mapError(
104
+ (cause) => new CliFailure({
105
+ message: `${file} is not a credentials file: ${oneLine(cause.message)}`,
106
+ exitCode: EXIT_FAILURE
107
+ })
108
+ )
109
+ );
110
+ });
111
+ const write = (secrets) => Effect2.gen(function* () {
112
+ const text = yield* Schema2.encodeUnknownEffect(SecretsJson)(
113
+ secrets
114
+ ).pipe(Effect2.mapError(storageFailure("encode", file)));
115
+ yield* fs.makeDirectory(pathService.dirname(file), { recursive: true }).pipe(Effect2.mapError(storageFailure("create", file)));
116
+ const temporary = `${file}.tmp`;
117
+ yield* fs.writeFileString(temporary, text, { mode: 384 }).pipe(Effect2.mapError(storageFailure("write", file)));
118
+ yield* fs.chmod(temporary, 384).pipe(Effect2.mapError(storageFailure("write", file)));
119
+ yield* fs.rename(temporary, file).pipe(Effect2.mapError(storageFailure("write", file)));
120
+ });
121
+ const update = (change) => Effect2.gen(function* () {
122
+ yield* write(change(yield* read()));
123
+ });
124
+ return Secrets.of({
125
+ get: (account) => read().pipe(Effect2.map((all) => Option2.fromNullishOr(all[account]))),
126
+ set: (account, secret) => update((all) => R.set(all, account, secret)),
127
+ remove: (account) => update((all) => R.remove(all, account))
128
+ });
104
129
  })
105
- });
130
+ );
106
131
  var accountKey = (origin, email) => `${normalizeOrigin(origin)}|${email}`;
107
132
  var normalizeOrigin = (origin) => Str3.replace(/\/+$/, "")(origin);
108
133
  var storageFailure = (action, path) => (cause) => new CliFailure({
@@ -152,8 +177,8 @@ var storeCredential = (credential, label = Option2.none()) => Effect2.gen(functi
152
177
  }
153
178
  const origin = normalizeOrigin(credential.origin);
154
179
  const key = accountKey(origin, credential.email);
155
- const keychain = yield* Keychain;
156
- yield* keychain.set(key, credential.secret);
180
+ const secrets = yield* Secrets;
181
+ yield* secrets.set(key, credential.secret);
157
182
  const config = yield* loadConfig();
158
183
  const account = new Account({
159
184
  origin,
package/dist/auth.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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, a keychain refusal. */\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 { Entry } from \"@napi-rs/keyring\";\nimport { 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\n/** The keychain service every Braven surface writes under. */\nexport const KEYCHAIN_SERVICE = \"braven\";\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/** The slice of an OS keychain the store uses; tests substitute an in-memory map. */\nexport interface KeychainShape {\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 OS keychain (macOS Keychain, Windows Credential Manager, libsecret). */\nexport class Keychain extends Context.Service<Keychain, KeychainShape>()(\n \"braven/Keychain\",\n) {}\n\nconst entry = (account: string): Entry => new Entry(KEYCHAIN_SERVICE, account);\n\nconst keychainAction = <A>(action: string, run: () => A) =>\n Effect.try({\n try: run,\n catch: (cause) =>\n new CliFailure({\n message: `keychain ${action} failed: ${oneLine(cause)}`,\n exitCode: EXIT_FAILURE,\n }),\n });\n\n/**\n * The real keychain. There is no plaintext fallback: when the backend refuses,\n * the failure surfaces and nothing is written anywhere else.\n */\nexport const keychainLayer: Layer.Layer<Keychain> = Layer.succeed(Keychain)({\n get: (account) =>\n keychainAction(\"read\", () => entry(account).getPassword()).pipe(\n Effect.map(Option.fromNullishOr),\n ),\n set: (account, secret) =>\n keychainAction(\"write\", () => {\n entry(account).setPassword(secret);\n }),\n remove: (account) =>\n keychainAction(\"delete\", () => {\n entry(account).deletePassword();\n }),\n});\n\n/** The config `current` value and the keychain account name 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 * Puts a secret in the keychain, records its account, and makes it current.\n *\n * The keychain write happens first; a failing keychain aborts before the\n * config 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 | Keychain | 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 keychain = yield* Keychain;\n yield* keychain.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 | Keychain | 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), keychain 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 | Keychain | 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 keychain = yield* Keychain;\n yield* keychain.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 | Keychain | 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 keychain = yield* Keychain;\n const secret = yield* keychain.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 keychain.\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 | Keychain | 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 keychain-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 Keychain,\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 | Keychain | 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\n | Sessions\n | StoreConfig\n | Keychain\n | FileSystem.FileSystem\n | 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", "Effect", "Layer", "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"]
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
7
  }