@braid-cloud/cli 0.1.17 → 0.1.19

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/dist/index.js CHANGED
@@ -18,10 +18,28 @@ var init_esm_shims = __esm({
18
18
  }
19
19
  });
20
20
 
21
+ // src/lib/automation-headers.ts
22
+ import process3 from "process";
23
+ var VERCEL_AUTOMATION_BYPASS_SECRET, mergeAutomationHeaders;
24
+ var init_automation_headers = __esm({
25
+ "src/lib/automation-headers.ts"() {
26
+ "use strict";
27
+ init_esm_shims();
28
+ VERCEL_AUTOMATION_BYPASS_SECRET = process3.env.VERCEL_AUTOMATION_BYPASS_SECRET;
29
+ mergeAutomationHeaders = (headers) => {
30
+ const merged = new Headers(headers);
31
+ if (VERCEL_AUTOMATION_BYPASS_SECRET) {
32
+ merged.set("x-vercel-protection-bypass", VERCEL_AUTOMATION_BYPASS_SECRET);
33
+ }
34
+ return merged.entries().next().done ? void 0 : merged;
35
+ };
36
+ }
37
+ });
38
+
21
39
  // src/lib/braid-workspace.ts
22
40
  import { existsSync, readFileSync } from "fs";
23
41
  import { dirname as dirname3, join as join2 } from "path";
24
- import process3 from "process";
42
+ import process4 from "process";
25
43
  function readPackageName(packagePath) {
26
44
  if (!existsSync(packagePath)) {
27
45
  return void 0;
@@ -38,7 +56,7 @@ function readPackageName(packagePath) {
38
56
  function isBraidWorkspaceRoot(directory) {
39
57
  return readPackageName(join2(directory, "package.json")) === BRAID_WORKSPACE_NAME;
40
58
  }
41
- function findBraidWorkspaceRoot(startDir = process3.cwd()) {
59
+ function findBraidWorkspaceRoot(startDir = process4.cwd()) {
42
60
  let currentDir = startDir;
43
61
  while (true) {
44
62
  if (isBraidWorkspaceRoot(currentDir)) {
@@ -108,9 +126,9 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
108
126
  import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
109
127
  import { homedir as homedir2 } from "os";
110
128
  import { dirname as dirname4, join as join3, parse } from "path";
111
- import process4 from "process";
129
+ import process5 from "process";
112
130
  import { Data as Data2, Effect as Effect3, pipe as pipe3 } from "effect";
113
- var CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILENAME, USER_CONFIG_FILENAME, CONVEX_CLOUD_SUFFIX_REGEX, LOCAL_CONVEX_PORT_REGEX, LOCAL_SERVER_ENV_FILES, LOOPBACK_HOSTS, NEWLINE_REGEX, TRAILING_SLASHES, ConfigReadError, ConfigWriteError, findConfigFile, findProjectConfigFile, findUserConfigFile, stripQuotes, parseDotenv, normalizeBaseUrl, resolveConvexSiteUrl, resolveLocalServerUrlFromEnv, resolveLocalServerUrlFromFiles, resolveLocalServerUrl, loadProjectConfig, loadUserConfig, resolveUserConfigWritePath, resolveProjectConfigWritePath, saveUserConfig, saveProjectConfig, isValidServerUrl, resolveServerUrlFromConfig, applyConfigSource, applyEnvOverrides, createDefaultMergedConfig, loadMergedConfig, loadConfig, saveConfig, getApiKey, setApiKey, getServerUrl, clearApiKey, getDemoContext, setDemoContext, clearDemoContext, loadConfigAsync, loadProjectConfigAsync, loadUserConfigAsync, loadMergedConfigAsync, findProjectConfigFileAsync, findUserConfigFileAsync, saveConfigAsync, saveUserConfigAsync, saveProjectConfigAsync, getApiKeyAsync, setApiKeyAsync, persistApiKeyAsync, getServerUrlAsync, clearApiKeyAsync, getDemoContextAsync, setDemoContextAsync, clearDemoContextAsync;
131
+ var CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILENAME, USER_CONFIG_FILENAME, CONVEX_CLOUD_SUFFIX_REGEX, LOCAL_CONVEX_PORT_REGEX, LOCAL_SERVER_ENV_FILES, LOOPBACK_HOSTS, NEWLINE_REGEX, TRAILING_SLASHES, ConfigReadError, ConfigWriteError, findConfigFile, findProjectConfigFile, findUserConfigFile, stripQuotes, parseDotenv, normalizeBaseUrl, resolveConvexSiteUrl, resolveLocalServerUrlFromEnv, resolveLocalServerUrlFromFiles, resolveLocalServerUrl, loadProjectConfig, loadUserConfig, resolveUserConfigWritePath, resolveProjectConfigWritePath, saveUserConfig, saveProjectConfig, isValidServerUrl, resolveServerUrlFromConfig, applyConfigSource, applyGlobalConfigSource, applyEnvOverrides, createDefaultMergedConfig, loadMergedConfig, loadConfig, saveConfig, getApiKey, setApiKey, getServerUrl, clearApiKey, getDemoContext, setDemoContext, clearDemoContext, loadConfigAsync, loadProjectConfigAsync, loadUserConfigAsync, loadMergedConfigAsync, findProjectConfigFileAsync, findUserConfigFileAsync, saveConfigAsync, saveUserConfigAsync, saveProjectConfigAsync, getApiKeyAsync, setApiKeyAsync, persistApiKeyAsync, getServerUrlAsync, clearApiKeyAsync, getDemoContextAsync, setDemoContextAsync, clearDemoContextAsync;
114
132
  var init_config = __esm({
115
133
  "src/lib/config.ts"() {
116
134
  "use strict";
@@ -133,7 +151,7 @@ var init_config = __esm({
133
151
  };
134
152
  ConfigWriteError = class extends Data2.TaggedError("ConfigWriteError") {
135
153
  };
136
- findConfigFile = (filename, startDir = process4.cwd()) => {
154
+ findConfigFile = (filename, startDir = process5.cwd()) => {
137
155
  let currentDir = startDir;
138
156
  while (true) {
139
157
  const configPath = join3(currentDir, filename);
@@ -147,8 +165,8 @@ var init_config = __esm({
147
165
  currentDir = parsed.dir;
148
166
  }
149
167
  };
150
- findProjectConfigFile = (startDir = process4.cwd()) => findConfigFile(PROJECT_CONFIG_FILENAME, startDir);
151
- findUserConfigFile = (startDir = process4.cwd()) => findConfigFile(USER_CONFIG_FILENAME, startDir);
168
+ findProjectConfigFile = (startDir = process5.cwd()) => findConfigFile(PROJECT_CONFIG_FILENAME, startDir);
169
+ findUserConfigFile = (startDir = process5.cwd()) => findConfigFile(USER_CONFIG_FILENAME, startDir);
152
170
  stripQuotes = (value) => {
153
171
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
154
172
  return value.slice(1, -1);
@@ -207,7 +225,7 @@ var init_config = __esm({
207
225
  }
208
226
  return void 0;
209
227
  };
210
- resolveLocalServerUrlFromFiles = (startDir = process4.cwd()) => {
228
+ resolveLocalServerUrlFromFiles = (startDir = process5.cwd()) => {
211
229
  const workspaceRoot = findBraidWorkspaceRoot(startDir);
212
230
  if (!workspaceRoot) {
213
231
  return void 0;
@@ -226,7 +244,7 @@ var init_config = __esm({
226
244
  }
227
245
  return void 0;
228
246
  };
229
- resolveLocalServerUrl = (startDir = process4.cwd()) => resolveLocalServerUrlFromEnv(process4.env) ?? resolveLocalServerUrlFromFiles(startDir);
247
+ resolveLocalServerUrl = (startDir = process5.cwd()) => resolveLocalServerUrlFromEnv(process5.env) ?? resolveLocalServerUrlFromFiles(startDir);
230
248
  loadProjectConfig = () => Effect3.tryPromise({
231
249
  try: async () => {
232
250
  const configPath = await findProjectConfigFile();
@@ -249,9 +267,9 @@ var init_config = __esm({
249
267
  },
250
268
  catch: () => void 0
251
269
  }).pipe(Effect3.orElseSucceed(() => void 0));
252
- resolveUserConfigWritePath = (startDir = process4.cwd()) => findUserConfigFile(startDir) ?? join3(startDir, USER_CONFIG_FILENAME);
253
- resolveProjectConfigWritePath = (startDir = process4.cwd()) => findProjectConfigFile(startDir) ?? join3(startDir, PROJECT_CONFIG_FILENAME);
254
- saveUserConfig = (config, startDir = process4.cwd()) => {
270
+ resolveUserConfigWritePath = (startDir = process5.cwd()) => findUserConfigFile(startDir) ?? join3(startDir, USER_CONFIG_FILENAME);
271
+ resolveProjectConfigWritePath = (startDir = process5.cwd()) => findProjectConfigFile(startDir) ?? join3(startDir, PROJECT_CONFIG_FILENAME);
272
+ saveUserConfig = (config, startDir = process5.cwd()) => {
255
273
  const targetPath = resolveUserConfigWritePath(startDir);
256
274
  return pipe3(
257
275
  Effect3.tryPromise({
@@ -267,7 +285,7 @@ var init_config = __esm({
267
285
  })
268
286
  );
269
287
  };
270
- saveProjectConfig = (config, startDir = process4.cwd()) => {
288
+ saveProjectConfig = (config, startDir = process5.cwd()) => {
271
289
  const targetPath = resolveProjectConfigWritePath(startDir);
272
290
  return pipe3(
273
291
  Effect3.tryPromise({
@@ -343,11 +361,17 @@ var init_config = __esm({
343
361
  merged.token = config.token;
344
362
  }
345
363
  };
364
+ applyGlobalConfigSource = (merged, config) => {
365
+ const serverUrl = resolveServerUrlFromConfig(config);
366
+ if (serverUrl) {
367
+ merged.serverUrl = serverUrl;
368
+ }
369
+ };
346
370
  applyEnvOverrides = (merged) => {
347
- if (process4.env.BRAID_API_KEY) {
348
- merged.token = process4.env.BRAID_API_KEY;
371
+ if (process5.env.BRAID_API_KEY) {
372
+ merged.token = process5.env.BRAID_API_KEY;
349
373
  }
350
- const envServerUrl = process4.env.BRAID_SKILLS_SERVER_URL ?? process4.env.BRAID_SERVER_URL;
374
+ const envServerUrl = process5.env.BRAID_SKILLS_SERVER_URL ?? process5.env.BRAID_SERVER_URL;
351
375
  if (envServerUrl && isValidServerUrl(envServerUrl)) {
352
376
  merged.serverUrl = envServerUrl;
353
377
  }
@@ -367,6 +391,7 @@ var init_config = __esm({
367
391
  }),
368
392
  Effect3.map(({ projectConfig, userConfig, globalConfig }) => {
369
393
  const merged = createDefaultMergedConfig();
394
+ applyGlobalConfigSource(merged, globalConfig);
370
395
  applyConfigSource(merged, projectConfig);
371
396
  applyConfigSource(merged, userConfig);
372
397
  applyEnvOverrides(merged);
@@ -402,7 +427,7 @@ var init_config = __esm({
402
427
  })
403
428
  );
404
429
  getApiKey = () => pipe3(
405
- Effect3.succeed(process4.env.BRAID_API_KEY),
430
+ Effect3.succeed(process5.env.BRAID_API_KEY),
406
431
  Effect3.flatMap(
407
432
  (envKey) => envKey ? Effect3.succeed(envKey) : pipe3(
408
433
  loadUserConfig(),
@@ -420,7 +445,7 @@ var init_config = __esm({
420
445
  Effect3.flatMap((config) => saveConfig({ ...config, apiKey }))
421
446
  );
422
447
  getServerUrl = () => pipe3(
423
- Effect3.succeed(process4.env.BRAID_SERVER_URL),
448
+ Effect3.succeed(process5.env.BRAID_SERVER_URL),
424
449
  Effect3.flatMap(
425
450
  (envUrl) => envUrl ? Effect3.succeed(envUrl) : pipe3(
426
451
  loadUserConfig(),
@@ -478,8 +503,13 @@ var init_config = __esm({
478
503
  saveProjectConfigAsync = (config, startDir) => Effect3.runPromise(saveProjectConfig(config, startDir));
479
504
  getApiKeyAsync = () => Effect3.runPromise(getApiKey());
480
505
  setApiKeyAsync = (apiKey) => Effect3.runPromise(setApiKey(apiKey));
481
- persistApiKeyAsync = async (apiKey) => {
482
- await setApiKeyAsync(apiKey);
506
+ persistApiKeyAsync = async (apiKey, serverUrl) => {
507
+ const config = await loadConfigAsync();
508
+ await saveConfigAsync({
509
+ ...config,
510
+ apiKey,
511
+ ...serverUrl ? { serverUrl } : {}
512
+ });
483
513
  const existingUserConfig = await loadUserConfigAsync();
484
514
  if (existingUserConfig?.token) {
485
515
  await saveUserConfigAsync({ ...existingUserConfig, token: apiKey });
@@ -493,88 +523,226 @@ var init_config = __esm({
493
523
  }
494
524
  });
495
525
 
496
- // src/lib/api.ts
497
- import { Data as Data3, Effect as Effect4, pipe as pipe4 } from "effect";
498
- var TRAILING_SLASH_REGEX, ALLOW_UNTRUSTED_SERVER_ENV, isTruthy, isLocalHost, isTrustedApiServerUrl, ApiError, AuthenticationError, NetworkError, resolveApiKey, resolveServerUrl, normalizeApiServerBaseUrl, parseResponse, buildApiUrl, buildExportUrl, executeApiRequest, parseScopeOptionsResponse, fetchScopeOptions, buildLibraryOptionsUrl, fetchLibraryOptions, fetchSkills, runLifecycleCommand, DEFAULT_PUBLIC_SERVER_URL, NotFoundError, RateLimitedError, handlePublicApiResponse, fetchPublicMetadata, fetchPublicExport, fetchPublicMetadataAsync, fetchPublicExportAsync, validateApiKey, fetchSkillsAsync, validateApiKeyAsync, fetchScopeOptionsAsync, fetchLibraryOptionsAsync, runLifecycleCommandAsync;
499
- var init_api = __esm({
500
- "src/lib/api.ts"() {
526
+ // src/lib/device-auth.ts
527
+ import { execFile } from "child_process";
528
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
529
+ import { hostname, platform } from "os";
530
+ import { join as join4 } from "path";
531
+ import process6 from "process";
532
+ function resolveAuthWebBaseUrl(serverUrl) {
533
+ const canonicalBaseUrl = resolveCanonicalAuthWebBaseUrl(serverUrl);
534
+ try {
535
+ const url = new URL(canonicalBaseUrl);
536
+ const localAuthBaseUrl = resolveLocalAuthWebBaseUrl();
537
+ if (localAuthBaseUrl && (DEFAULT_BRAID_AUTH_HOSTS.has(url.hostname) || isLoopbackHost(url.hostname))) {
538
+ return localAuthBaseUrl;
539
+ }
540
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
541
+ } catch {
542
+ return canonicalBaseUrl;
543
+ }
544
+ }
545
+ function buildAuthVerificationUrlFromBaseUrl(authBaseUrl, userCode) {
546
+ return `${authBaseUrl.replace(TRAILING_SLASHES2, "")}/cli/authorize?user_code=${encodeURIComponent(userCode)}`;
547
+ }
548
+ async function fetchAuthConfigFromBaseUrl(authBaseUrl) {
549
+ const headers = mergeAutomationHeaders();
550
+ const authConfigUrl = `${authBaseUrl}/api/cli/auth-config`;
551
+ const response = headers ? await fetch(authConfigUrl, { headers }) : await fetch(authConfigUrl);
552
+ if (!response.ok) {
553
+ const body = await response.text();
554
+ throw new Error(
555
+ `Failed to fetch auth config (${response.status}): ${body}`
556
+ );
557
+ }
558
+ return {
559
+ ...await response.json(),
560
+ verificationBaseUrl: authBaseUrl
561
+ };
562
+ }
563
+ async function fetchAuthConfig(serverUrl) {
564
+ const authBaseUrl = resolveAuthWebBaseUrl(serverUrl).replace(
565
+ TRAILING_SLASHES2,
566
+ ""
567
+ );
568
+ const canonicalAuthBaseUrl = resolveCanonicalAuthWebBaseUrl(
569
+ serverUrl
570
+ ).replace(TRAILING_SLASHES2, "");
571
+ try {
572
+ return await fetchAuthConfigFromBaseUrl(authBaseUrl);
573
+ } catch (error) {
574
+ if (authBaseUrl !== canonicalAuthBaseUrl && isHostedBraidAuthServer(serverUrl)) {
575
+ return fetchAuthConfigFromBaseUrl(canonicalAuthBaseUrl);
576
+ }
577
+ throw error;
578
+ }
579
+ }
580
+ async function initiateDeviceAuth(convexSiteUrl, deviceInfo) {
581
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/authorize`;
582
+ const response = await fetch(url, {
583
+ method: "POST",
584
+ headers: mergeAutomationHeaders({
585
+ "Content-Type": "application/json"
586
+ }),
587
+ body: JSON.stringify(deviceInfo)
588
+ });
589
+ if (!response.ok) {
590
+ const body = await response.text();
591
+ throw new Error(
592
+ `Failed to initiate device authorization (${response.status}): ${body}`
593
+ );
594
+ }
595
+ return response.json();
596
+ }
597
+ async function pollForSession(convexSiteUrl, deviceCode, interval, expiresIn, timeoutSeconds) {
598
+ const tokenUrl = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/token`;
599
+ const deadline = Date.now() + Math.min(expiresIn, timeoutSeconds) * 1e3;
600
+ let currentInterval = interval;
601
+ while (Date.now() < deadline) {
602
+ await sleep(currentInterval * 1e3);
603
+ const response = await fetch(tokenUrl, {
604
+ method: "POST",
605
+ headers: mergeAutomationHeaders({
606
+ "Content-Type": "application/json"
607
+ }),
608
+ body: JSON.stringify({ device_code: deviceCode })
609
+ });
610
+ if (response.ok) {
611
+ const result = await response.json();
612
+ return {
613
+ sessionToken: result.session_token,
614
+ expiresAt: result.expires_at,
615
+ user: result.user
616
+ };
617
+ }
618
+ let errorCode;
619
+ try {
620
+ const errorBody = await response.json();
621
+ errorCode = errorBody.error;
622
+ } catch {
623
+ throw new Error(`Device auth polling error: HTTP ${response.status}`);
624
+ }
625
+ if (errorCode === "authorization_pending") {
626
+ continue;
627
+ }
628
+ if (errorCode === "slow_down") {
629
+ currentInterval += 5;
630
+ continue;
631
+ }
632
+ if (errorCode === "expired_token") {
633
+ throw new DeviceAuthExpiredError();
634
+ }
635
+ if (errorCode === "access_denied") {
636
+ throw new DeviceAuthDeniedError();
637
+ }
638
+ throw new Error(
639
+ `Device auth polling error: ${errorCode ?? `HTTP ${response.status}`}`
640
+ );
641
+ }
642
+ throw new DeviceAuthTimeoutError();
643
+ }
644
+ async function fetchSessionInfo(convexSiteUrl, sessionToken) {
645
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
646
+ const response = await fetch(url, {
647
+ method: "GET",
648
+ headers: mergeAutomationHeaders({
649
+ Authorization: `Bearer ${sessionToken}`
650
+ })
651
+ });
652
+ if (response.status === 404) {
653
+ return null;
654
+ }
655
+ if (!response.ok) {
656
+ const body = await response.text();
657
+ throw new Error(
658
+ `Failed to fetch session info (${response.status}): ${body}`
659
+ );
660
+ }
661
+ return response.json();
662
+ }
663
+ async function revokeSession(convexSiteUrl, sessionToken) {
664
+ const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
665
+ const response = await fetch(url, {
666
+ method: "DELETE",
667
+ headers: mergeAutomationHeaders({
668
+ Authorization: `Bearer ${sessionToken}`
669
+ })
670
+ });
671
+ if (response.status === 404) {
672
+ return false;
673
+ }
674
+ if (!response.ok) {
675
+ const body = await response.text();
676
+ throw new Error(`Failed to revoke session (${response.status}): ${body}`);
677
+ }
678
+ return true;
679
+ }
680
+ function openBrowser(url) {
681
+ if (process6.env.BRAID_DISABLE_BROWSER_OPEN === "1" || process6.env.BRAID_DISABLE_BROWSER_OPEN === "true") {
682
+ return;
683
+ }
684
+ const currentPlatform = platform();
685
+ if (currentPlatform === "darwin") {
686
+ execFile("open", [url], noop);
687
+ } else if (currentPlatform === "win32") {
688
+ execFile("cmd", ["/c", "start", "", url], noop);
689
+ } else {
690
+ execFile("xdg-open", [url], noop);
691
+ }
692
+ }
693
+ function getDeviceInfo() {
694
+ return {
695
+ deviceName: hostname(),
696
+ deviceOs: platform(),
697
+ deviceHostname: hostname()
698
+ };
699
+ }
700
+ var TRAILING_SLASHES2, LEGACY_BRAID_AUTH_HOSTS, BRAID_APP_HOST, DEFAULT_BRAID_AUTH_HOSTS, LOOPBACK_HOSTS2, LOCAL_AUTH_ENV_FILES, LOCAL_AUTH_ENV_KEYS, NEWLINE_REGEX2, DeviceAuthTimeoutError, DeviceAuthDeniedError, DeviceAuthExpiredError, sleep, normalizeBaseUrl2, normalizeLoopbackBaseUrl, isLoopbackHost, isLoopbackUrl, stripQuotes2, parseDotenv2, resolveLocalAuthWebBaseUrlFromEnv, resolveLocalAuthWebBaseUrlFromFiles, resolveLocalAuthWebBaseUrl, normalizeServerBaseUrl, resolveCanonicalAuthWebBaseUrl, isHostedBraidAuthServer, noop;
701
+ var init_device_auth = __esm({
702
+ "src/lib/device-auth.ts"() {
501
703
  "use strict";
502
704
  init_esm_shims();
503
- init_config();
504
- TRAILING_SLASH_REGEX = /\/$/;
505
- ALLOW_UNTRUSTED_SERVER_ENV = "BRAID_ALLOW_UNTRUSTED_SERVER_URL";
506
- isTruthy = (value) => value === "1" || value === "true" || value === "yes";
507
- isLocalHost = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
508
- isTrustedApiServerUrl = (serverUrl) => {
509
- try {
510
- const parsed = new URL(serverUrl);
511
- const hostname2 = parsed.hostname;
512
- const isBraidHost = hostname2 === "braid.cloud" || hostname2.endsWith(".braid.cloud");
513
- if (parsed.protocol === "https:" && isBraidHost) {
514
- return true;
515
- }
516
- if (parsed.protocol === "http:" && isLocalHost(hostname2)) {
517
- return true;
518
- }
519
- return false;
520
- } catch {
521
- return false;
705
+ init_automation_headers();
706
+ init_braid_workspace();
707
+ TRAILING_SLASHES2 = /\/+$/;
708
+ LEGACY_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
709
+ "api.braid.cloud",
710
+ "braid.cloud",
711
+ "www.braid.cloud"
712
+ ]);
713
+ BRAID_APP_HOST = "app.braid.cloud";
714
+ DEFAULT_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
715
+ ...LEGACY_BRAID_AUTH_HOSTS,
716
+ BRAID_APP_HOST
717
+ ]);
718
+ LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
719
+ LOCAL_AUTH_ENV_FILES = [
720
+ ["apps", "web", ".env.local"],
721
+ ["apps", "convex", ".env.local"]
722
+ ];
723
+ LOCAL_AUTH_ENV_KEYS = ["APP_URL", "SITE_URL"];
724
+ NEWLINE_REGEX2 = /\r?\n/;
725
+ DeviceAuthTimeoutError = class extends Error {
726
+ constructor() {
727
+ super("Device authorization timed out");
728
+ this.name = "DeviceAuthTimeoutError";
522
729
  }
523
730
  };
524
- ApiError = class extends Data3.TaggedError("ApiError") {
525
- };
526
- AuthenticationError = class extends Data3.TaggedError("AuthenticationError") {
527
- };
528
- NetworkError = class extends Data3.TaggedError("NetworkError") {
529
- };
530
- resolveApiKey = (optionsApiKey) => {
531
- if (optionsApiKey) {
532
- return Effect4.succeed(optionsApiKey);
731
+ DeviceAuthDeniedError = class extends Error {
732
+ constructor() {
733
+ super("Device authorization was denied");
734
+ this.name = "DeviceAuthDeniedError";
533
735
  }
534
- return pipe4(
535
- Effect4.tryPromise({
536
- try: () => getApiKeyAsync(),
537
- catch: () => new NetworkError({ message: "Failed to read config" })
538
- }),
539
- Effect4.flatMap(
540
- (key) => key ? Effect4.succeed(key) : Effect4.fail(
541
- new AuthenticationError({
542
- message: 'No API key configured. Run "braid auth" to authenticate.'
543
- })
544
- )
545
- )
546
- );
547
736
  };
548
- resolveServerUrl = (optionsServerUrl) => {
549
- if (optionsServerUrl) {
550
- if (!(isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(optionsServerUrl))) {
551
- return Effect4.fail(
552
- new NetworkError({
553
- message: `Untrusted server URL '${optionsServerUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
554
- })
555
- );
556
- }
557
- return Effect4.succeed(optionsServerUrl);
737
+ DeviceAuthExpiredError = class extends Error {
738
+ constructor() {
739
+ super("Device authorization code has expired");
740
+ this.name = "DeviceAuthExpiredError";
558
741
  }
559
- return pipe4(
560
- Effect4.tryPromise({
561
- try: () => getServerUrlAsync(),
562
- catch: () => new NetworkError({ message: "Failed to read config" })
563
- }),
564
- Effect4.flatMap((serverUrl) => {
565
- if (isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(serverUrl)) {
566
- return Effect4.succeed(serverUrl);
567
- }
568
- return Effect4.fail(
569
- new NetworkError({
570
- message: `Untrusted server URL '${serverUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
571
- })
572
- );
573
- })
574
- );
575
742
  };
576
- normalizeApiServerBaseUrl = (serverUrl) => {
577
- const trimmed = serverUrl.replace(TRAILING_SLASH_REGEX, "");
743
+ sleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
744
+ normalizeBaseUrl2 = (rawUrl) => {
745
+ const trimmed = rawUrl.replace(TRAILING_SLASHES2, "");
578
746
  try {
579
747
  const url = new URL(trimmed);
580
748
  return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
@@ -582,13 +750,295 @@ var init_api = __esm({
582
750
  return trimmed;
583
751
  }
584
752
  };
753
+ normalizeLoopbackBaseUrl = (rawUrl) => {
754
+ const normalized = normalizeBaseUrl2(rawUrl);
755
+ try {
756
+ const url = new URL(normalized);
757
+ if (isLoopbackHost(url.hostname)) {
758
+ url.hostname = "localhost";
759
+ }
760
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
761
+ } catch {
762
+ return normalized;
763
+ }
764
+ };
765
+ isLoopbackHost = (hostname2) => LOOPBACK_HOSTS2.has(hostname2);
766
+ isLoopbackUrl = (rawUrl) => {
767
+ try {
768
+ return isLoopbackHost(new URL(rawUrl).hostname);
769
+ } catch {
770
+ return false;
771
+ }
772
+ };
773
+ stripQuotes2 = (value) => {
774
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
775
+ return value.slice(1, -1);
776
+ }
777
+ return value;
778
+ };
779
+ parseDotenv2 = (content) => {
780
+ const result = {};
781
+ for (const rawLine of content.split(NEWLINE_REGEX2)) {
782
+ const line = rawLine.trim();
783
+ if (!line || line.startsWith("#")) {
784
+ continue;
785
+ }
786
+ const separatorIndex = line.indexOf("=");
787
+ if (separatorIndex <= 0) {
788
+ continue;
789
+ }
790
+ const key = line.slice(0, separatorIndex).trim();
791
+ const value = line.slice(separatorIndex + 1).trim();
792
+ result[key] = stripQuotes2(value);
793
+ }
794
+ return result;
795
+ };
796
+ resolveLocalAuthWebBaseUrlFromEnv = (env) => {
797
+ for (const key of LOCAL_AUTH_ENV_KEYS) {
798
+ const value = env[key];
799
+ if (value && isLoopbackUrl(value)) {
800
+ return normalizeLoopbackBaseUrl(value);
801
+ }
802
+ }
803
+ return void 0;
804
+ };
805
+ resolveLocalAuthWebBaseUrlFromFiles = () => {
806
+ const workspaceRoot = findBraidWorkspaceRoot();
807
+ if (!workspaceRoot) {
808
+ return void 0;
809
+ }
810
+ for (const envFile of LOCAL_AUTH_ENV_FILES) {
811
+ const envPath = join4(workspaceRoot, ...envFile);
812
+ if (!existsSync3(envPath)) {
813
+ continue;
814
+ }
815
+ const localAppUrl = resolveLocalAuthWebBaseUrlFromEnv(
816
+ parseDotenv2(readFileSync3(envPath, "utf8"))
817
+ );
818
+ if (localAppUrl) {
819
+ return localAppUrl;
820
+ }
821
+ }
822
+ return void 0;
823
+ };
824
+ resolveLocalAuthWebBaseUrl = () => resolveLocalAuthWebBaseUrlFromEnv(process6.env) ?? resolveLocalAuthWebBaseUrlFromFiles();
825
+ normalizeServerBaseUrl = (serverUrl) => {
826
+ const trimmed = normalizeBaseUrl2(serverUrl);
827
+ try {
828
+ const url = new URL(trimmed);
829
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
830
+ } catch {
831
+ return trimmed;
832
+ }
833
+ };
834
+ resolveCanonicalAuthWebBaseUrl = (serverUrl) => {
835
+ const normalized = normalizeServerBaseUrl(serverUrl);
836
+ try {
837
+ const url = new URL(normalized);
838
+ if (LEGACY_BRAID_AUTH_HOSTS.has(url.hostname)) {
839
+ url.hostname = BRAID_APP_HOST;
840
+ url.pathname = "/";
841
+ }
842
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
843
+ } catch {
844
+ return normalized;
845
+ }
846
+ };
847
+ isHostedBraidAuthServer = (serverUrl) => {
848
+ try {
849
+ return DEFAULT_BRAID_AUTH_HOSTS.has(
850
+ new URL(normalizeServerBaseUrl(serverUrl)).hostname
851
+ );
852
+ } catch {
853
+ return false;
854
+ }
855
+ };
856
+ noop = () => {
857
+ };
858
+ }
859
+ });
860
+
861
+ // src/lib/api.ts
862
+ import { Data as Data3, Effect as Effect4, pipe as pipe4 } from "effect";
863
+ var TRAILING_SLASH_REGEX, ALLOW_UNTRUSTED_SERVER_ENV, SESSION_TOKEN_PREFIX, isTruthy, isLocalHost, isConvexSiteHost, isTrustedApiServerUrl, isSessionApiServerUrl, REAUTH_REQUIRED_MESSAGE, RAW_AUTH_ERROR_PATTERN, normalizeAuthenticationErrorMessage, ApiError, AuthenticationError, NetworkError, resolveApiKey, resolveServerUrl, resolveApiRequestServerUrl, normalizeApiServerBaseUrl, readJsonResponseAsync, readJsonResponse, getApiErrorResponse, isAuthenticationErrorResponse, parseResponse, buildApiUrl, buildExportUrl, executeApiRequest, parseScopeOptionsResponse, fetchScopeOptions, buildLibraryOptionsUrl, fetchLibraryOptions, fetchSkills, runLifecycleCommand, DEFAULT_PUBLIC_SERVER_URL, NotFoundError, RateLimitedError, handlePublicApiResponse, fetchPublicMetadata, fetchPublicExport, fetchPublicMetadataAsync, fetchPublicExportAsync, validateApiKey, fetchSkillsAsync, validateApiKeyAsync, fetchScopeOptionsAsync, fetchLibraryOptionsAsync, runLifecycleCommandAsync;
864
+ var init_api = __esm({
865
+ "src/lib/api.ts"() {
866
+ "use strict";
867
+ init_esm_shims();
868
+ init_automation_headers();
869
+ init_config();
870
+ init_device_auth();
871
+ TRAILING_SLASH_REGEX = /\/$/;
872
+ ALLOW_UNTRUSTED_SERVER_ENV = "BRAID_ALLOW_UNTRUSTED_SERVER_URL";
873
+ SESSION_TOKEN_PREFIX = "brs_";
874
+ isTruthy = (value) => value === "1" || value === "true" || value === "yes";
875
+ isLocalHost = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
876
+ isConvexSiteHost = (hostname2) => hostname2.endsWith(".convex.site");
877
+ isTrustedApiServerUrl = (serverUrl) => {
878
+ try {
879
+ const parsed = new URL(serverUrl);
880
+ const hostname2 = parsed.hostname;
881
+ const isBraidHost = hostname2 === "braid.cloud" || hostname2.endsWith(".braid.cloud");
882
+ const isConvexSite = isConvexSiteHost(hostname2);
883
+ if (parsed.protocol === "https:" && (isBraidHost || isConvexSite)) {
884
+ return true;
885
+ }
886
+ if (parsed.protocol === "http:" && isLocalHost(hostname2)) {
887
+ return true;
888
+ }
889
+ return false;
890
+ } catch {
891
+ return false;
892
+ }
893
+ };
894
+ isSessionApiServerUrl = (serverUrl) => {
895
+ try {
896
+ const hostname2 = new URL(serverUrl).hostname;
897
+ return isConvexSiteHost(hostname2) || isLocalHost(hostname2);
898
+ } catch {
899
+ return false;
900
+ }
901
+ };
902
+ REAUTH_REQUIRED_MESSAGE = "Invalid or expired API key. Run 'braid auth' to re-authenticate.";
903
+ RAW_AUTH_ERROR_PATTERN = /invalid authentication token|authentication token expired|token expired|not authenticated/i;
904
+ normalizeAuthenticationErrorMessage = (message) => {
905
+ if (!message || RAW_AUTH_ERROR_PATTERN.test(message)) {
906
+ return REAUTH_REQUIRED_MESSAGE;
907
+ }
908
+ return message;
909
+ };
910
+ ApiError = class extends Data3.TaggedError("ApiError") {
911
+ };
912
+ AuthenticationError = class extends Data3.TaggedError("AuthenticationError") {
913
+ };
914
+ NetworkError = class extends Data3.TaggedError("NetworkError") {
915
+ };
916
+ resolveApiKey = (optionsApiKey) => {
917
+ if (optionsApiKey) {
918
+ return Effect4.succeed(optionsApiKey);
919
+ }
920
+ return pipe4(
921
+ Effect4.tryPromise({
922
+ try: () => getApiKeyAsync(),
923
+ catch: () => new NetworkError({ message: "Failed to read config" })
924
+ }),
925
+ Effect4.flatMap(
926
+ (key) => key ? Effect4.succeed(key) : Effect4.fail(
927
+ new AuthenticationError({
928
+ message: 'No API key configured. Run "braid auth" to authenticate.'
929
+ })
930
+ )
931
+ )
932
+ );
933
+ };
934
+ resolveServerUrl = (optionsServerUrl) => {
935
+ if (optionsServerUrl) {
936
+ if (!(isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(optionsServerUrl))) {
937
+ return Effect4.fail(
938
+ new NetworkError({
939
+ message: `Untrusted server URL '${optionsServerUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
940
+ })
941
+ );
942
+ }
943
+ return Effect4.succeed(optionsServerUrl);
944
+ }
945
+ return pipe4(
946
+ Effect4.tryPromise({
947
+ try: () => getServerUrlAsync(),
948
+ catch: () => new NetworkError({ message: "Failed to read config" })
949
+ }),
950
+ Effect4.flatMap((serverUrl) => {
951
+ if (isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(serverUrl)) {
952
+ return Effect4.succeed(serverUrl);
953
+ }
954
+ return Effect4.fail(
955
+ new NetworkError({
956
+ message: `Untrusted server URL '${serverUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
957
+ })
958
+ );
959
+ })
960
+ );
961
+ };
962
+ resolveApiRequestServerUrl = (serverUrl, apiKey) => {
963
+ if (!apiKey?.startsWith(SESSION_TOKEN_PREFIX)) {
964
+ return Effect4.succeed(serverUrl);
965
+ }
966
+ if (isSessionApiServerUrl(serverUrl)) {
967
+ return Effect4.succeed(serverUrl);
968
+ }
969
+ return pipe4(
970
+ Effect4.tryPromise({
971
+ try: async () => {
972
+ const authConfig = await fetchAuthConfig(serverUrl);
973
+ return authConfig.convexSiteUrl.replace(TRAILING_SLASH_REGEX, "");
974
+ },
975
+ catch: (cause) => new NetworkError({
976
+ message: `Failed to resolve session API server for ${serverUrl}`,
977
+ cause
978
+ })
979
+ }),
980
+ Effect4.flatMap((resolvedServerUrl) => {
981
+ if (isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(resolvedServerUrl)) {
982
+ return Effect4.succeed(resolvedServerUrl);
983
+ }
984
+ return Effect4.fail(
985
+ new NetworkError({
986
+ message: `Untrusted server URL '${resolvedServerUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
987
+ })
988
+ );
989
+ })
990
+ );
991
+ };
992
+ normalizeApiServerBaseUrl = (serverUrl) => {
993
+ const trimmed = serverUrl.replace(TRAILING_SLASH_REGEX, "");
994
+ try {
995
+ const url = new URL(trimmed);
996
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
997
+ } catch {
998
+ return trimmed;
999
+ }
1000
+ };
1001
+ readJsonResponseAsync = async (response) => {
1002
+ const raw = await response.text();
1003
+ if (raw.trim().length === 0) {
1004
+ if (response.ok) {
1005
+ throw new Error("Empty API response");
1006
+ }
1007
+ return null;
1008
+ }
1009
+ try {
1010
+ return JSON.parse(raw);
1011
+ } catch (error) {
1012
+ if (!response.ok) {
1013
+ return null;
1014
+ }
1015
+ throw error;
1016
+ }
1017
+ };
1018
+ readJsonResponse = (response) => Effect4.tryPromise({
1019
+ try: () => readJsonResponseAsync(response),
1020
+ catch: () => new NetworkError({ message: "Failed to parse API response" })
1021
+ });
1022
+ getApiErrorResponse = (json) => {
1023
+ if (typeof json !== "object" || json === null || Array.isArray(json)) {
1024
+ return {};
1025
+ }
1026
+ return json;
1027
+ };
1028
+ isAuthenticationErrorResponse = (response, json) => {
1029
+ if (response.status === 401) {
1030
+ return true;
1031
+ }
1032
+ const errorResponse = getApiErrorResponse(json);
1033
+ return RAW_AUTH_ERROR_PATTERN.test(errorResponse.error ?? "");
1034
+ };
585
1035
  parseResponse = (response, json) => {
586
1036
  if (!response.ok) {
587
- const errorResponse = json;
588
- if (response.status === 401) {
1037
+ const errorResponse = getApiErrorResponse(json);
1038
+ if (isAuthenticationErrorResponse(response, json)) {
589
1039
  return Effect4.fail(
590
1040
  new AuthenticationError({
591
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1041
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
592
1042
  })
593
1043
  );
594
1044
  }
@@ -647,10 +1097,10 @@ var init_api = __esm({
647
1097
  Effect4.tryPromise({
648
1098
  try: () => fetch(url.toString(), {
649
1099
  method: "GET",
650
- headers: {
1100
+ headers: mergeAutomationHeaders({
651
1101
  "Content-Type": "application/json",
652
1102
  ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
653
- }
1103
+ })
654
1104
  }),
655
1105
  catch: (e) => new NetworkError({
656
1106
  message: `Failed to connect to ${serverUrl}`,
@@ -660,7 +1110,7 @@ var init_api = __esm({
660
1110
  Effect4.flatMap(
661
1111
  (response) => pipe4(
662
1112
  Effect4.tryPromise({
663
- try: () => response.json(),
1113
+ try: () => readJsonResponseAsync(response),
664
1114
  catch: () => new NetworkError({ message: "Failed to parse API response" })
665
1115
  }),
666
1116
  Effect4.flatMap((json) => parseResponse(response, json))
@@ -669,11 +1119,11 @@ var init_api = __esm({
669
1119
  );
670
1120
  parseScopeOptionsResponse = (response, json) => {
671
1121
  if (!response.ok) {
672
- const errorResponse = json;
673
- if (response.status === 401) {
1122
+ const errorResponse = getApiErrorResponse(json);
1123
+ if (isAuthenticationErrorResponse(response, json)) {
674
1124
  return Effect4.fail(
675
1125
  new AuthenticationError({
676
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1126
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
677
1127
  })
678
1128
  );
679
1129
  }
@@ -692,33 +1142,37 @@ var init_api = __esm({
692
1142
  apiKey: resolveApiKey(options.apiKey),
693
1143
  serverUrl: resolveServerUrl(options.serverUrl)
694
1144
  }),
695
- Effect4.flatMap(({ apiKey, serverUrl }) => {
696
- const url = buildApiUrl(serverUrl, "/api/skills/scope-options");
697
- return pipe4(
698
- Effect4.tryPromise({
699
- try: () => fetch(url.toString(), {
700
- method: "GET",
701
- headers: {
702
- Authorization: `Bearer ${apiKey}`,
703
- "Content-Type": "application/json"
704
- }
705
- }),
706
- catch: (e) => new NetworkError({
707
- message: `Failed to connect to ${serverUrl}`,
708
- cause: e
709
- })
710
- }),
711
- Effect4.flatMap(
712
- (response) => pipe4(
1145
+ Effect4.flatMap(
1146
+ ({ apiKey, serverUrl }) => pipe4(
1147
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1148
+ Effect4.flatMap((apiServerUrl) => {
1149
+ const url = buildApiUrl(apiServerUrl, "/api/skills/scope-options");
1150
+ return pipe4(
713
1151
  Effect4.tryPromise({
714
- try: () => response.json(),
715
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1152
+ try: () => fetch(url.toString(), {
1153
+ method: "GET",
1154
+ headers: mergeAutomationHeaders({
1155
+ Authorization: `Bearer ${apiKey}`,
1156
+ "Content-Type": "application/json"
1157
+ })
1158
+ }),
1159
+ catch: (e) => new NetworkError({
1160
+ message: `Failed to connect to ${apiServerUrl}`,
1161
+ cause: e
1162
+ })
716
1163
  }),
717
- Effect4.flatMap((json) => parseScopeOptionsResponse(response, json))
718
- )
719
- )
720
- );
721
- })
1164
+ Effect4.flatMap(
1165
+ (response) => pipe4(
1166
+ readJsonResponse(response),
1167
+ Effect4.flatMap(
1168
+ (json) => parseScopeOptionsResponse(response, json)
1169
+ )
1170
+ )
1171
+ )
1172
+ );
1173
+ })
1174
+ )
1175
+ )
722
1176
  );
723
1177
  buildLibraryOptionsUrl = (serverUrl, options) => {
724
1178
  const url = buildApiUrl(serverUrl, "/api/skills/library-options");
@@ -752,41 +1206,57 @@ var init_api = __esm({
752
1206
  }),
753
1207
  Effect4.flatMap(
754
1208
  ({ apiKey, serverUrl }) => pipe4(
755
- Effect4.tryPromise({
756
- try: () => fetch(buildLibraryOptionsUrl(serverUrl, options).toString(), {
757
- method: "GET",
758
- headers: {
759
- Authorization: `Bearer ${apiKey}`,
760
- "Content-Type": "application/json"
761
- }
762
- }),
763
- catch: (e) => new NetworkError({
764
- message: `Failed to connect to ${serverUrl}`,
765
- cause: e
766
- })
767
- }),
1209
+ resolveApiRequestServerUrl(serverUrl, apiKey),
768
1210
  Effect4.flatMap(
769
- (response) => pipe4(
1211
+ (apiServerUrl) => pipe4(
770
1212
  Effect4.tryPromise({
771
- try: () => response.json(),
772
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1213
+ try: () => fetch(
1214
+ buildLibraryOptionsUrl(apiServerUrl, options).toString(),
1215
+ {
1216
+ method: "GET",
1217
+ headers: mergeAutomationHeaders({
1218
+ Authorization: `Bearer ${apiKey}`,
1219
+ "Content-Type": "application/json"
1220
+ })
1221
+ }
1222
+ ),
1223
+ catch: (e) => new NetworkError({
1224
+ message: `Failed to connect to ${apiServerUrl}`,
1225
+ cause: e
1226
+ })
773
1227
  }),
774
- Effect4.flatMap((json) => {
775
- if (!response.ok) {
776
- return parseResponse(response, json).pipe(
777
- Effect4.flatMap(
778
- () => Effect4.fail(
779
- new ApiError({
780
- message: "API request failed",
781
- code: "UNKNOWN_ERROR",
782
- status: response.status
783
- })
784
- )
785
- )
786
- );
787
- }
788
- return Effect4.succeed(json);
789
- })
1228
+ Effect4.flatMap(
1229
+ (response) => pipe4(
1230
+ readJsonResponse(response),
1231
+ Effect4.flatMap(
1232
+ (json) => {
1233
+ if (!response.ok) {
1234
+ return pipe4(
1235
+ parseResponse(response, json),
1236
+ Effect4.flatMap(
1237
+ () => Effect4.fail(
1238
+ new ApiError({
1239
+ message: "API request failed",
1240
+ code: "UNKNOWN_ERROR",
1241
+ status: response.status
1242
+ })
1243
+ )
1244
+ ),
1245
+ Effect4.catchTag(
1246
+ "AuthenticationError",
1247
+ (error) => Effect4.fail(error)
1248
+ ),
1249
+ Effect4.catchTag(
1250
+ "ApiError",
1251
+ (error) => Effect4.fail(error)
1252
+ )
1253
+ );
1254
+ }
1255
+ return Effect4.succeed(json);
1256
+ }
1257
+ )
1258
+ )
1259
+ )
790
1260
  )
791
1261
  )
792
1262
  )
@@ -797,10 +1267,15 @@ var init_api = __esm({
797
1267
  apiKey: options.demo ? Effect4.succeed(options.apiKey) : resolveApiKey(options.apiKey),
798
1268
  serverUrl: resolveServerUrl(options.serverUrl)
799
1269
  }),
800
- Effect4.flatMap(({ apiKey, serverUrl }) => {
801
- const url = buildExportUrl(serverUrl, options);
802
- return executeApiRequest(url, apiKey, serverUrl);
803
- })
1270
+ Effect4.flatMap(
1271
+ ({ apiKey, serverUrl }) => pipe4(
1272
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1273
+ Effect4.flatMap((apiServerUrl) => {
1274
+ const url = buildExportUrl(apiServerUrl, options);
1275
+ return executeApiRequest(url, apiKey, apiServerUrl);
1276
+ })
1277
+ )
1278
+ )
804
1279
  );
805
1280
  runLifecycleCommand = (request, options = {}) => pipe4(
806
1281
  Effect4.all({
@@ -808,45 +1283,56 @@ var init_api = __esm({
808
1283
  serverUrl: resolveServerUrl(options.serverUrl)
809
1284
  }),
810
1285
  Effect4.flatMap(
811
- ({ apiKey, serverUrl }) => Effect4.tryPromise({
812
- try: async () => {
813
- const response = await fetch(
814
- buildApiUrl(serverUrl, "/api/lifecycle").toString(),
815
- {
816
- method: "POST",
817
- headers: {
818
- "Content-Type": "application/json",
819
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
820
- },
821
- body: JSON.stringify({ ...request, demo: options.demo })
822
- }
823
- );
824
- const json = await response.json();
825
- if (!response.ok) {
826
- const errorResponse = json;
827
- if (response.status === 401) {
828
- throw new AuthenticationError({
829
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1286
+ ({ apiKey, serverUrl }) => pipe4(
1287
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1288
+ Effect4.flatMap(
1289
+ (apiServerUrl) => Effect4.tryPromise({
1290
+ try: async () => {
1291
+ const response = await fetch(
1292
+ buildApiUrl(apiServerUrl, "/api/lifecycle").toString(),
1293
+ {
1294
+ method: "POST",
1295
+ headers: mergeAutomationHeaders({
1296
+ "Content-Type": "application/json",
1297
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
1298
+ }),
1299
+ body: JSON.stringify({ ...request, demo: options.demo })
1300
+ }
1301
+ );
1302
+ const json = await readJsonResponseAsync(response).catch(() => {
1303
+ throw new NetworkError({
1304
+ message: "Failed to parse API response"
1305
+ });
1306
+ });
1307
+ if (!response.ok) {
1308
+ const errorResponse = getApiErrorResponse(json);
1309
+ if (isAuthenticationErrorResponse(response, json)) {
1310
+ throw new AuthenticationError({
1311
+ message: normalizeAuthenticationErrorMessage(
1312
+ errorResponse.error
1313
+ )
1314
+ });
1315
+ }
1316
+ throw new ApiError({
1317
+ message: errorResponse.error || "API request failed",
1318
+ code: errorResponse.code || "UNKNOWN_ERROR",
1319
+ status: response.status
1320
+ });
1321
+ }
1322
+ return json;
1323
+ },
1324
+ catch: (error) => {
1325
+ if (error instanceof ApiError || error instanceof AuthenticationError || error instanceof NetworkError) {
1326
+ return error;
1327
+ }
1328
+ return new NetworkError({
1329
+ message: `Failed to connect to ${apiServerUrl}`,
1330
+ cause: error
830
1331
  });
831
1332
  }
832
- throw new ApiError({
833
- message: errorResponse.error || "API request failed",
834
- code: errorResponse.code || "UNKNOWN_ERROR",
835
- status: response.status
836
- });
837
- }
838
- return json;
839
- },
840
- catch: (error) => {
841
- if (error instanceof ApiError || error instanceof AuthenticationError || error instanceof NetworkError) {
842
- return error;
843
- }
844
- return new NetworkError({
845
- message: `Failed to connect to ${serverUrl}`,
846
- cause: error
847
- });
848
- }
849
- })
1333
+ })
1334
+ )
1335
+ )
850
1336
  )
851
1337
  );
852
1338
  DEFAULT_PUBLIC_SERVER_URL = "https://braid.cloud";
@@ -873,7 +1359,7 @@ var init_api = __esm({
873
1359
  })
874
1360
  );
875
1361
  }
876
- const errorResponse = json;
1362
+ const errorResponse = getApiErrorResponse(json);
877
1363
  return Effect4.fail(
878
1364
  new ApiError({
879
1365
  message: errorResponse.error || "API request failed",
@@ -893,7 +1379,10 @@ var init_api = __esm({
893
1379
  }
894
1380
  return pipe4(
895
1381
  Effect4.tryPromise({
896
- try: () => fetch(url.toString(), { method: "GET" }),
1382
+ try: () => fetch(url.toString(), {
1383
+ method: "GET",
1384
+ headers: mergeAutomationHeaders()
1385
+ }),
897
1386
  catch: (e) => new NetworkError({
898
1387
  message: `Failed to connect to ${baseUrl}`,
899
1388
  cause: e
@@ -901,10 +1390,7 @@ var init_api = __esm({
901
1390
  }),
902
1391
  Effect4.flatMap(
903
1392
  (response) => pipe4(
904
- Effect4.tryPromise({
905
- try: () => response.json(),
906
- catch: () => new NetworkError({ message: "Failed to parse API response" })
907
- }),
1393
+ readJsonResponse(response),
908
1394
  Effect4.flatMap(
909
1395
  (json) => handlePublicApiResponse(response, json)
910
1396
  )
@@ -926,7 +1412,10 @@ var init_api = __esm({
926
1412
  }
927
1413
  return pipe4(
928
1414
  Effect4.tryPromise({
929
- try: () => fetch(url.toString(), { method: "GET" }),
1415
+ try: () => fetch(url.toString(), {
1416
+ method: "GET",
1417
+ headers: mergeAutomationHeaders()
1418
+ }),
930
1419
  catch: (e) => new NetworkError({
931
1420
  message: `Failed to connect to ${baseUrl}`,
932
1421
  cause: e
@@ -934,10 +1423,7 @@ var init_api = __esm({
934
1423
  }),
935
1424
  Effect4.flatMap(
936
1425
  (response) => pipe4(
937
- Effect4.tryPromise({
938
- try: () => response.json(),
939
- catch: () => new NetworkError({ message: "Failed to parse API response" })
940
- }),
1426
+ readJsonResponse(response),
941
1427
  Effect4.flatMap(
942
1428
  (json) => handlePublicApiResponse(response, json)
943
1429
  )
@@ -969,10 +1455,10 @@ var init_api = __esm({
969
1455
  url.searchParams.set("profile", "default");
970
1456
  const response = await fetch(url.toString(), {
971
1457
  method: "GET",
972
- headers: {
1458
+ headers: mergeAutomationHeaders({
973
1459
  Authorization: `Bearer ${apiKey}`,
974
1460
  "Content-Type": "application/json"
975
- }
1461
+ })
976
1462
  });
977
1463
  return response.status !== 401;
978
1464
  },
@@ -992,7 +1478,7 @@ var init_api = __esm({
992
1478
  });
993
1479
 
994
1480
  // src/lib/tui.ts
995
- import process5 from "process";
1481
+ import process7 from "process";
996
1482
  import {
997
1483
  cancel as clackCancel,
998
1484
  confirm as clackConfirm,
@@ -1017,19 +1503,19 @@ function spinner() {
1017
1503
  return {
1018
1504
  start: (message) => {
1019
1505
  if (message) {
1020
- process5.stdout.write(`${message}
1506
+ process7.stdout.write(`${message}
1021
1507
  `);
1022
1508
  }
1023
1509
  },
1024
1510
  stop: (message) => {
1025
1511
  if (message) {
1026
- process5.stdout.write(`${message}
1512
+ process7.stdout.write(`${message}
1027
1513
  `);
1028
1514
  }
1029
1515
  },
1030
1516
  message: (message) => {
1031
1517
  if (message) {
1032
- process5.stdout.write(`${message}
1518
+ process7.stdout.write(`${message}
1033
1519
  `);
1034
1520
  }
1035
1521
  }
@@ -1039,7 +1525,7 @@ function intro(message) {
1039
1525
  if (isTTY) {
1040
1526
  clackIntro(message);
1041
1527
  } else {
1042
- process5.stdout.write(`
1528
+ process7.stdout.write(`
1043
1529
  ${message}
1044
1530
  `);
1045
1531
  }
@@ -1048,7 +1534,7 @@ function outro(message) {
1048
1534
  if (isTTY) {
1049
1535
  clackOutro(message);
1050
1536
  } else {
1051
- process5.stdout.write(`${message}
1537
+ process7.stdout.write(`${message}
1052
1538
 
1053
1539
  `);
1054
1540
  }
@@ -1058,7 +1544,7 @@ var init_tui = __esm({
1058
1544
  "src/lib/tui.ts"() {
1059
1545
  "use strict";
1060
1546
  init_esm_shims();
1061
- isTTY = Boolean(process5.stdout.isTTY);
1547
+ isTTY = Boolean(process7.stdout.isTTY);
1062
1548
  cancel = clackCancel;
1063
1549
  confirm = clackConfirm;
1064
1550
  isCancel = clackIsCancel;
@@ -1168,10 +1654,10 @@ var scope_exports = {};
1168
1654
  __export(scope_exports, {
1169
1655
  scopeCommand: () => scopeCommand
1170
1656
  });
1171
- import process7 from "process";
1657
+ import process9 from "process";
1172
1658
  function exitCancelled(message) {
1173
1659
  cancel(message);
1174
- process7.exit(0);
1660
+ process9.exit(0);
1175
1661
  }
1176
1662
  async function selectTargetFile(options) {
1177
1663
  if (options.file) {
@@ -1523,7 +2009,7 @@ async function scopeCommand(options) {
1523
2009
  } catch (error) {
1524
2010
  loadSpinner.stop("Failed to load scope options");
1525
2011
  log.error(error instanceof Error ? error.message : String(error));
1526
- process7.exit(1);
2012
+ process9.exit(1);
1527
2013
  }
1528
2014
  }
1529
2015
  var init_scope = __esm({
@@ -1562,7 +2048,7 @@ __export(lockfile_exports, {
1562
2048
  });
1563
2049
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
1564
2050
  import { join as join6 } from "path";
1565
- import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2051
+ import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
1566
2052
  var LOCKFILE_FILENAME, LOCKFILE_VERSION, MARKETPLACE_KEY_REGEX, ORG_KEY_REGEX, LockfileReadError, LockfileWriteError, LockfileVersionError, LockfileCorruptError, getLockfilePath, emptyLockfile, readLockfile, writeLockfile, resolveLocked, upsertLockfileEntry, removeLockfileEntry, buildLockfileKey, parseLockfileKey, readLockfileAsync, writeLockfileAsync, upsertLockfileEntryAsync, removeLockfileEntryAsync;
1567
2053
  var init_lockfile = __esm({
1568
2054
  "src/lib/lockfile.ts"() {
@@ -1572,13 +2058,13 @@ var init_lockfile = __esm({
1572
2058
  LOCKFILE_VERSION = 1;
1573
2059
  MARKETPLACE_KEY_REGEX = /^@([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1574
2060
  ORG_KEY_REGEX = /^([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1575
- LockfileReadError = class extends Data4.TaggedError("LockfileReadError") {
2061
+ LockfileReadError = class extends Data5.TaggedError("LockfileReadError") {
1576
2062
  };
1577
- LockfileWriteError = class extends Data4.TaggedError("LockfileWriteError") {
2063
+ LockfileWriteError = class extends Data5.TaggedError("LockfileWriteError") {
1578
2064
  };
1579
- LockfileVersionError = class extends Data4.TaggedError("LockfileVersionError") {
2065
+ LockfileVersionError = class extends Data5.TaggedError("LockfileVersionError") {
1580
2066
  };
1581
- LockfileCorruptError = class extends Data4.TaggedError("LockfileCorruptError") {
2067
+ LockfileCorruptError = class extends Data5.TaggedError("LockfileCorruptError") {
1582
2068
  };
1583
2069
  getLockfilePath = (dir) => join6(dir, LOCKFILE_FILENAME);
1584
2070
  emptyLockfile = () => ({
@@ -1587,27 +2073,27 @@ var init_lockfile = __esm({
1587
2073
  });
1588
2074
  readLockfile = (dir) => {
1589
2075
  const lockfilePath = getLockfilePath(dir);
1590
- return pipe5(
1591
- Effect5.tryPromise({
2076
+ return pipe6(
2077
+ Effect6.tryPromise({
1592
2078
  try: () => readFile3(lockfilePath, "utf-8"),
1593
2079
  catch: () => new LockfileReadError({ path: lockfilePath, cause: "not found" })
1594
2080
  }),
1595
- Effect5.catchTag(
2081
+ Effect6.catchTag(
1596
2082
  "LockfileReadError",
1597
- () => Effect5.succeed(null)
2083
+ () => Effect6.succeed(null)
1598
2084
  ),
1599
- Effect5.flatMap((content) => {
2085
+ Effect6.flatMap((content) => {
1600
2086
  if (content === null) {
1601
- return Effect5.succeed(null);
2087
+ return Effect6.succeed(null);
1602
2088
  }
1603
- return pipe5(
1604
- Effect5.try({
2089
+ return pipe6(
2090
+ Effect6.try({
1605
2091
  try: () => JSON.parse(content),
1606
2092
  catch: (cause) => new LockfileCorruptError({ path: lockfilePath, cause })
1607
2093
  }),
1608
- Effect5.flatMap((parsed) => {
2094
+ Effect6.flatMap((parsed) => {
1609
2095
  if (parsed.lockfileVersion !== LOCKFILE_VERSION) {
1610
- return Effect5.fail(
2096
+ return Effect6.fail(
1611
2097
  new LockfileVersionError({
1612
2098
  path: lockfilePath,
1613
2099
  expected: LOCKFILE_VERSION,
@@ -1615,7 +2101,7 @@ var init_lockfile = __esm({
1615
2101
  })
1616
2102
  );
1617
2103
  }
1618
- return Effect5.succeed(parsed);
2104
+ return Effect6.succeed(parsed);
1619
2105
  })
1620
2106
  );
1621
2107
  })
@@ -1623,7 +2109,7 @@ var init_lockfile = __esm({
1623
2109
  };
1624
2110
  writeLockfile = (dir, lockfile) => {
1625
2111
  const lockfilePath = getLockfilePath(dir);
1626
- return Effect5.tryPromise({
2112
+ return Effect6.tryPromise({
1627
2113
  try: () => writeFile4(
1628
2114
  lockfilePath,
1629
2115
  `${JSON.stringify(lockfile, null, 2)}
@@ -1640,26 +2126,26 @@ var init_lockfile = __esm({
1640
2126
  }
1641
2127
  return entry;
1642
2128
  };
1643
- upsertLockfileEntry = (dir, key, entry) => pipe5(
2129
+ upsertLockfileEntry = (dir, key, entry) => pipe6(
1644
2130
  readLockfile(dir),
1645
- Effect5.map((existing) => existing ?? emptyLockfile()),
1646
- Effect5.map((lockfile) => ({
2131
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2132
+ Effect6.map((lockfile) => ({
1647
2133
  ...lockfile,
1648
2134
  resolved: {
1649
2135
  ...lockfile.resolved,
1650
2136
  [key]: entry
1651
2137
  }
1652
2138
  })),
1653
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2139
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1654
2140
  );
1655
- removeLockfileEntry = (dir, key) => pipe5(
2141
+ removeLockfileEntry = (dir, key) => pipe6(
1656
2142
  readLockfile(dir),
1657
- Effect5.map((existing) => existing ?? emptyLockfile()),
1658
- Effect5.map((lockfile) => {
2143
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2144
+ Effect6.map((lockfile) => {
1659
2145
  const { [key]: _removed, ...rest } = lockfile.resolved;
1660
2146
  return { ...lockfile, resolved: rest };
1661
2147
  }),
1662
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2148
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1663
2149
  );
1664
2150
  buildLockfileKey = (source, handle, slug, version) => {
1665
2151
  const prefix = source === "marketplace" ? `@${handle}` : handle;
@@ -1686,10 +2172,10 @@ var init_lockfile = __esm({
1686
2172
  }
1687
2173
  return null;
1688
2174
  };
1689
- readLockfileAsync = (dir) => Effect5.runPromise(readLockfile(dir));
1690
- writeLockfileAsync = (dir, lockfile) => Effect5.runPromise(writeLockfile(dir, lockfile));
1691
- upsertLockfileEntryAsync = (dir, key, entry) => Effect5.runPromise(upsertLockfileEntry(dir, key, entry));
1692
- removeLockfileEntryAsync = (dir, key) => Effect5.runPromise(removeLockfileEntry(dir, key));
2175
+ readLockfileAsync = (dir) => Effect6.runPromise(readLockfile(dir));
2176
+ writeLockfileAsync = (dir, lockfile) => Effect6.runPromise(writeLockfile(dir, lockfile));
2177
+ upsertLockfileEntryAsync = (dir, key, entry) => Effect6.runPromise(upsertLockfileEntry(dir, key, entry));
2178
+ removeLockfileEntryAsync = (dir, key) => Effect6.runPromise(removeLockfileEntry(dir, key));
1693
2179
  }
1694
2180
  });
1695
2181
 
@@ -2274,25 +2760,260 @@ var resolveHookConfigPath = (agent, options) => {
2274
2760
 
2275
2761
  // src/commands/agents.ts
2276
2762
  init_api();
2763
+
2764
+ // src/lib/command-output.ts
2765
+ init_esm_shims();
2766
+ init_api();
2767
+ import process8 from "process";
2768
+
2769
+ // src/lib/marketplace-api.ts
2770
+ init_esm_shims();
2771
+ init_automation_headers();
2772
+ init_config();
2773
+ init_device_auth();
2774
+ import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2775
+ var TRAILING_SLASH_REGEX2 = /\/$/;
2776
+ var SESSION_TOKEN_PREFIX2 = "brs_";
2777
+ var MarketplaceApiError = class extends Data4.TaggedError("MarketplaceApiError") {
2778
+ };
2779
+ var isLocalHost2 = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
2780
+ var isConvexSiteHost2 = (hostname2) => hostname2.endsWith(".convex.site");
2781
+ var isSessionApiServerUrl2 = (serverUrl) => {
2782
+ try {
2783
+ const hostname2 = new URL(serverUrl).hostname;
2784
+ return isConvexSiteHost2(hostname2) || isLocalHost2(hostname2);
2785
+ } catch {
2786
+ return false;
2787
+ }
2788
+ };
2789
+ var resolveApiKey2 = (provided) => {
2790
+ if (provided) {
2791
+ return Effect5.succeed(provided);
2792
+ }
2793
+ return pipe5(
2794
+ Effect5.tryPromise({
2795
+ try: () => getApiKeyAsync(),
2796
+ catch: () => new MarketplaceApiError({
2797
+ message: "Failed to load API key",
2798
+ status: 500,
2799
+ code: "CONFIG_ERROR"
2800
+ })
2801
+ }),
2802
+ Effect5.flatMap(
2803
+ (token) => token ? Effect5.succeed(token) : Effect5.fail(
2804
+ new MarketplaceApiError({
2805
+ message: "Not signed in. Run 'braid auth'.",
2806
+ status: 401,
2807
+ code: "AUTH_REQUIRED"
2808
+ })
2809
+ )
2810
+ )
2811
+ );
2812
+ };
2813
+ var resolveServer = (provided) => {
2814
+ if (provided) {
2815
+ return Effect5.succeed(provided.replace(TRAILING_SLASH_REGEX2, ""));
2816
+ }
2817
+ return pipe5(
2818
+ Effect5.tryPromise({
2819
+ try: () => getServerUrlAsync(),
2820
+ catch: () => new MarketplaceApiError({
2821
+ message: "Failed to load server URL",
2822
+ status: 500,
2823
+ code: "CONFIG_ERROR"
2824
+ })
2825
+ }),
2826
+ Effect5.map((server) => server.replace(TRAILING_SLASH_REGEX2, ""))
2827
+ );
2828
+ };
2829
+ var resolveRequestServer = (server, apiKey) => {
2830
+ if (!apiKey.startsWith(SESSION_TOKEN_PREFIX2) || isSessionApiServerUrl2(server)) {
2831
+ return Effect5.succeed(server);
2832
+ }
2833
+ return Effect5.tryPromise({
2834
+ try: async () => {
2835
+ const authConfig = await fetchAuthConfig(server);
2836
+ return authConfig.convexSiteUrl.replace(TRAILING_SLASH_REGEX2, "");
2837
+ },
2838
+ catch: () => new MarketplaceApiError({
2839
+ message: `Failed to resolve marketplace API server for ${server}`,
2840
+ status: 500,
2841
+ code: "CONFIG_ERROR"
2842
+ })
2843
+ });
2844
+ };
2845
+ var requestJson = (url, options, requestOptions = {}) => pipe5(
2846
+ Effect5.tryPromise({
2847
+ try: () => fetch(url, options),
2848
+ catch: () => new MarketplaceApiError({
2849
+ message: "Network error",
2850
+ status: 503,
2851
+ code: "NETWORK_ERROR"
2852
+ })
2853
+ }),
2854
+ Effect5.flatMap(
2855
+ (response) => Effect5.tryPromise({
2856
+ try: async () => {
2857
+ const payload = await response.json().catch(() => null);
2858
+ if (!response.ok) {
2859
+ const normalizedMessage = requestOptions.normalizeErrorMessage?.({
2860
+ payload,
2861
+ response,
2862
+ url
2863
+ });
2864
+ throw new MarketplaceApiError({
2865
+ message: normalizedMessage ?? (response.status === 404 ? requestOptions.notFoundMessage : void 0) ?? payload?.error ?? payload?.message ?? `Request failed (HTTP ${response.status} from ${new URL(url).origin})`,
2866
+ status: response.status,
2867
+ code: payload?.code ?? "REQUEST_ERROR"
2868
+ });
2869
+ }
2870
+ return payload;
2871
+ },
2872
+ catch: (error) => error instanceof MarketplaceApiError ? error : new MarketplaceApiError({
2873
+ message: "Invalid response",
2874
+ status: 500,
2875
+ code: "PARSE_ERROR"
2876
+ })
2877
+ })
2878
+ )
2879
+ );
2880
+ var fetchMarketplaceLibrary = (options) => pipe5(
2881
+ Effect5.all({
2882
+ server: resolveServer(options.server),
2883
+ apiKey: resolveApiKey2(options.apiKey)
2884
+ }),
2885
+ Effect5.flatMap(
2886
+ ({ server, apiKey }) => Effect5.all({
2887
+ apiKey: Effect5.succeed(apiKey),
2888
+ server: resolveRequestServer(server, apiKey)
2889
+ })
2890
+ ),
2891
+ Effect5.flatMap(
2892
+ ({ server, apiKey }) => requestJson(
2893
+ `${server}/api/marketplace/library`,
2894
+ {
2895
+ method: "GET",
2896
+ headers: mergeAutomationHeaders({
2897
+ Authorization: `Bearer ${apiKey}`
2898
+ })
2899
+ }
2900
+ )
2901
+ ),
2902
+ Effect5.map((response) => response.items)
2903
+ );
2904
+ var fetchMarketplaceInstallManifest = (slug, options) => pipe5(
2905
+ Effect5.all({
2906
+ server: resolveServer(options.server),
2907
+ apiKey: resolveApiKey2(options.apiKey)
2908
+ }),
2909
+ Effect5.flatMap(
2910
+ ({ server, apiKey }) => Effect5.all({
2911
+ apiKey: Effect5.succeed(apiKey),
2912
+ server: resolveRequestServer(server, apiKey)
2913
+ })
2914
+ ),
2915
+ Effect5.flatMap(
2916
+ ({ server, apiKey }) => requestJson(
2917
+ `${server}/api/marketplace/install-manifest/${encodeURIComponent(slug)}`,
2918
+ {
2919
+ method: "GET",
2920
+ headers: mergeAutomationHeaders({
2921
+ Authorization: `Bearer ${apiKey}`
2922
+ })
2923
+ },
2924
+ {
2925
+ normalizeErrorMessage: ({ payload }) => payload?.error === "Not entitled" ? "Pack not found or no longer available in your library." : void 0,
2926
+ notFoundMessage: "Pack not found or no longer available in your library."
2927
+ }
2928
+ )
2929
+ )
2930
+ );
2931
+ var fetchMarketplaceLibraryAsync = (options) => Effect5.runPromise(fetchMarketplaceLibrary(options));
2932
+ var fetchMarketplaceInstallManifestAsync = (slug, options) => Effect5.runPromise(fetchMarketplaceInstallManifest(slug, options));
2933
+
2934
+ // src/lib/command-output.ts
2277
2935
  init_tui();
2278
- var parseCsv = (input) => {
2279
- if (!input) {
2280
- return void 0;
2936
+ var CommandValidationError = class extends Error {
2937
+ code = "CLI_VALIDATION_ERROR";
2938
+ constructor(message) {
2939
+ super(message);
2940
+ this.name = "CommandValidationError";
2281
2941
  }
2282
- const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2283
- return values.length > 0 ? values : void 0;
2284
2942
  };
2285
2943
  var writeJson = (value) => {
2286
- process.stdout.write(`${JSON.stringify(value, null, 2)}
2944
+ process8.stdout.write(`${JSON.stringify(value, null, 2)}
2287
2945
  `);
2288
2946
  };
2947
+ var toJsonErrorOutput = (error) => {
2948
+ if (error instanceof ApiError) {
2949
+ return {
2950
+ error: error.message,
2951
+ code: error.code,
2952
+ status: error.status
2953
+ };
2954
+ }
2955
+ if (error instanceof AuthenticationError) {
2956
+ return {
2957
+ error: error.message,
2958
+ code: "AUTH_REQUIRED",
2959
+ status: 401
2960
+ };
2961
+ }
2962
+ if (error instanceof NetworkError) {
2963
+ return {
2964
+ error: error.message,
2965
+ code: "NETWORK_ERROR",
2966
+ status: 503
2967
+ };
2968
+ }
2969
+ if (error instanceof MarketplaceApiError) {
2970
+ return {
2971
+ error: error.message,
2972
+ code: error.code,
2973
+ status: error.status
2974
+ };
2975
+ }
2976
+ if (error instanceof CommandValidationError || error instanceof SyntaxError) {
2977
+ return {
2978
+ error: error.message,
2979
+ code: "CLI_VALIDATION_ERROR",
2980
+ status: 400
2981
+ };
2982
+ }
2983
+ if (error instanceof Error) {
2984
+ return {
2985
+ error: error.message,
2986
+ code: "CLI_ERROR",
2987
+ status: 500
2988
+ };
2989
+ }
2990
+ return {
2991
+ error: String(error),
2992
+ code: "CLI_ERROR",
2993
+ status: 500
2994
+ };
2995
+ };
2289
2996
  var fail = (message) => {
2290
- throw new Error(message);
2997
+ throw new CommandValidationError(message);
2291
2998
  };
2292
- var exitWithError = (error) => {
2999
+ var exitCommandError = (error, options) => {
3000
+ if (options?.json) {
3001
+ writeJson(toJsonErrorOutput(error));
3002
+ process8.exit(1);
3003
+ }
2293
3004
  const message = error instanceof Error ? error.message : String(error);
2294
3005
  log.error(message);
2295
- process.exit(1);
3006
+ process8.exit(1);
3007
+ };
3008
+
3009
+ // src/commands/agents.ts
3010
+ init_tui();
3011
+ var parseCsv = (input) => {
3012
+ if (!input) {
3013
+ return void 0;
3014
+ }
3015
+ const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3016
+ return values.length > 0 ? values : void 0;
2296
3017
  };
2297
3018
  var run = (command, args, options) => {
2298
3019
  const apiOptions = {
@@ -2397,7 +3118,7 @@ async function agentsListCommand(options) {
2397
3118
  log.success("sub-agents list completed");
2398
3119
  writeJson(result);
2399
3120
  } catch (error) {
2400
- exitWithError(error);
3121
+ exitCommandError(error, { json: options.json });
2401
3122
  }
2402
3123
  }
2403
3124
  async function agentsGetCommand(options) {
@@ -2411,7 +3132,7 @@ async function agentsGetCommand(options) {
2411
3132
  log.success("sub-agents get completed");
2412
3133
  writeJson(result);
2413
3134
  } catch (error) {
2414
- exitWithError(error);
3135
+ exitCommandError(error, { json: options.json });
2415
3136
  }
2416
3137
  }
2417
3138
  async function agentsCreateCommand(options) {
@@ -2439,7 +3160,7 @@ async function agentsCreateCommand(options) {
2439
3160
  }
2440
3161
  log.success("sub-agents create completed");
2441
3162
  } catch (error) {
2442
- exitWithError(error);
3163
+ exitCommandError(error, { json: options.json });
2443
3164
  }
2444
3165
  }
2445
3166
  async function agentsUpdateCommand(options) {
@@ -2464,7 +3185,7 @@ async function agentsUpdateCommand(options) {
2464
3185
  }
2465
3186
  log.success("sub-agents update completed");
2466
3187
  } catch (error) {
2467
- exitWithError(error);
3188
+ exitCommandError(error, { json: options.json });
2468
3189
  }
2469
3190
  }
2470
3191
  async function agentsRemoveCommand(options) {
@@ -2480,7 +3201,7 @@ async function agentsRemoveCommand(options) {
2480
3201
  }
2481
3202
  log.success("sub-agents remove completed");
2482
3203
  } catch (error) {
2483
- exitWithError(error);
3204
+ exitCommandError(error, { json: options.json });
2484
3205
  }
2485
3206
  }
2486
3207
  async function agentsInstallCommand(options) {
@@ -2504,366 +3225,58 @@ async function agentsInstallCommand(options) {
2504
3225
  const result = await writeAgentsForPlatformAsync(
2505
3226
  target,
2506
3227
  [spec],
2507
- installPath
2508
- );
2509
- summary.push({
2510
- platform: target.id,
2511
- installPath,
2512
- written: result.written.length,
2513
- warnings: result.warnings,
2514
- errors: result.errors
2515
- });
2516
- }
2517
- if (options.json) {
2518
- writeJson(summary);
2519
- return;
2520
- }
2521
- for (const item of summary) {
2522
- log.info(
2523
- `${item.platform}: ${item.written} file(s) written${item.installPath ? ` to ${item.installPath}` : ""}`
2524
- );
2525
- for (const warning of item.warnings) {
2526
- log.warn(` warning: ${warning}`);
2527
- }
2528
- for (const error of item.errors) {
2529
- log.error(` error (${error.agent}): ${error.error}`);
2530
- }
2531
- }
2532
- log.success("sub-agents install completed");
2533
- } catch (error) {
2534
- exitWithError(error);
2535
- }
2536
- }
2537
-
2538
- // src/commands/auth.ts
2539
- init_esm_shims();
2540
- init_api();
2541
- init_config();
2542
- import process8 from "process";
2543
-
2544
- // src/lib/device-auth.ts
2545
- init_esm_shims();
2546
- init_braid_workspace();
2547
- import { execFile } from "child_process";
2548
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2549
- import { hostname, platform } from "os";
2550
- import { join as join4 } from "path";
2551
- import process6 from "process";
2552
- var TRAILING_SLASHES2 = /\/+$/;
2553
- var LEGACY_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
2554
- "api.braid.cloud",
2555
- "braid.cloud",
2556
- "www.braid.cloud"
2557
- ]);
2558
- var BRAID_APP_HOST = "app.braid.cloud";
2559
- var DEFAULT_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
2560
- ...LEGACY_BRAID_AUTH_HOSTS,
2561
- BRAID_APP_HOST
2562
- ]);
2563
- var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
2564
- var LOCAL_AUTH_ENV_FILES = [
2565
- ["apps", "web", ".env.local"],
2566
- ["apps", "convex", ".env.local"]
2567
- ];
2568
- var LOCAL_AUTH_ENV_KEYS = ["APP_URL", "SITE_URL"];
2569
- var NEWLINE_REGEX2 = /\r?\n/;
2570
- var DeviceAuthTimeoutError = class extends Error {
2571
- constructor() {
2572
- super("Device authorization timed out");
2573
- this.name = "DeviceAuthTimeoutError";
2574
- }
2575
- };
2576
- var DeviceAuthDeniedError = class extends Error {
2577
- constructor() {
2578
- super("Device authorization was denied");
2579
- this.name = "DeviceAuthDeniedError";
2580
- }
2581
- };
2582
- var DeviceAuthExpiredError = class extends Error {
2583
- constructor() {
2584
- super("Device authorization code has expired");
2585
- this.name = "DeviceAuthExpiredError";
2586
- }
2587
- };
2588
- var sleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
2589
- var normalizeBaseUrl2 = (rawUrl) => {
2590
- const trimmed = rawUrl.replace(TRAILING_SLASHES2, "");
2591
- try {
2592
- const url = new URL(trimmed);
2593
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2594
- } catch {
2595
- return trimmed;
2596
- }
2597
- };
2598
- var normalizeLoopbackBaseUrl = (rawUrl) => {
2599
- const normalized = normalizeBaseUrl2(rawUrl);
2600
- try {
2601
- const url = new URL(normalized);
2602
- if (isLoopbackHost(url.hostname)) {
2603
- url.hostname = "localhost";
2604
- }
2605
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2606
- } catch {
2607
- return normalized;
2608
- }
2609
- };
2610
- var isLoopbackHost = (hostname2) => LOOPBACK_HOSTS2.has(hostname2);
2611
- var isLoopbackUrl = (rawUrl) => {
2612
- try {
2613
- return isLoopbackHost(new URL(rawUrl).hostname);
2614
- } catch {
2615
- return false;
2616
- }
2617
- };
2618
- var stripQuotes2 = (value) => {
2619
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2620
- return value.slice(1, -1);
2621
- }
2622
- return value;
2623
- };
2624
- var parseDotenv2 = (content) => {
2625
- const result = {};
2626
- for (const rawLine of content.split(NEWLINE_REGEX2)) {
2627
- const line = rawLine.trim();
2628
- if (!line || line.startsWith("#")) {
2629
- continue;
2630
- }
2631
- const separatorIndex = line.indexOf("=");
2632
- if (separatorIndex <= 0) {
2633
- continue;
2634
- }
2635
- const key = line.slice(0, separatorIndex).trim();
2636
- const value = line.slice(separatorIndex + 1).trim();
2637
- result[key] = stripQuotes2(value);
2638
- }
2639
- return result;
2640
- };
2641
- var resolveLocalAuthWebBaseUrlFromEnv = (env) => {
2642
- for (const key of LOCAL_AUTH_ENV_KEYS) {
2643
- const value = env[key];
2644
- if (value && isLoopbackUrl(value)) {
2645
- return normalizeLoopbackBaseUrl(value);
2646
- }
2647
- }
2648
- return void 0;
2649
- };
2650
- var resolveLocalAuthWebBaseUrlFromFiles = () => {
2651
- const workspaceRoot = findBraidWorkspaceRoot();
2652
- if (!workspaceRoot) {
2653
- return void 0;
2654
- }
2655
- for (const envFile of LOCAL_AUTH_ENV_FILES) {
2656
- const envPath = join4(workspaceRoot, ...envFile);
2657
- if (!existsSync3(envPath)) {
2658
- continue;
2659
- }
2660
- const localAppUrl = resolveLocalAuthWebBaseUrlFromEnv(
2661
- parseDotenv2(readFileSync3(envPath, "utf8"))
2662
- );
2663
- if (localAppUrl) {
2664
- return localAppUrl;
2665
- }
2666
- }
2667
- return void 0;
2668
- };
2669
- var resolveLocalAuthWebBaseUrl = () => resolveLocalAuthWebBaseUrlFromEnv(process6.env) ?? resolveLocalAuthWebBaseUrlFromFiles();
2670
- var normalizeServerBaseUrl = (serverUrl) => {
2671
- const trimmed = normalizeBaseUrl2(serverUrl);
2672
- try {
2673
- const url = new URL(trimmed);
2674
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2675
- } catch {
2676
- return trimmed;
2677
- }
2678
- };
2679
- var resolveCanonicalAuthWebBaseUrl = (serverUrl) => {
2680
- const normalized = normalizeServerBaseUrl(serverUrl);
2681
- try {
2682
- const url = new URL(normalized);
2683
- if (LEGACY_BRAID_AUTH_HOSTS.has(url.hostname)) {
2684
- url.hostname = BRAID_APP_HOST;
2685
- url.pathname = "/";
2686
- }
2687
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2688
- } catch {
2689
- return normalized;
2690
- }
2691
- };
2692
- var isHostedBraidAuthServer = (serverUrl) => {
2693
- try {
2694
- return DEFAULT_BRAID_AUTH_HOSTS.has(
2695
- new URL(normalizeServerBaseUrl(serverUrl)).hostname
2696
- );
2697
- } catch {
2698
- return false;
2699
- }
2700
- };
2701
- function resolveAuthWebBaseUrl(serverUrl) {
2702
- const canonicalBaseUrl = resolveCanonicalAuthWebBaseUrl(serverUrl);
2703
- try {
2704
- const url = new URL(canonicalBaseUrl);
2705
- const localAuthBaseUrl = resolveLocalAuthWebBaseUrl();
2706
- if (localAuthBaseUrl && (DEFAULT_BRAID_AUTH_HOSTS.has(url.hostname) || isLoopbackHost(url.hostname))) {
2707
- return localAuthBaseUrl;
2708
- }
2709
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2710
- } catch {
2711
- return canonicalBaseUrl;
2712
- }
2713
- }
2714
- function buildAuthVerificationUrlFromBaseUrl(authBaseUrl, userCode) {
2715
- return `${authBaseUrl.replace(TRAILING_SLASHES2, "")}/cli/authorize?user_code=${encodeURIComponent(userCode)}`;
2716
- }
2717
- async function fetchAuthConfigFromBaseUrl(authBaseUrl) {
2718
- const response = await fetch(`${authBaseUrl}/api/cli/auth-config`);
2719
- if (!response.ok) {
2720
- const body = await response.text();
2721
- throw new Error(
2722
- `Failed to fetch auth config (${response.status}): ${body}`
2723
- );
2724
- }
2725
- return {
2726
- ...await response.json(),
2727
- verificationBaseUrl: authBaseUrl
2728
- };
2729
- }
2730
- async function fetchAuthConfig(serverUrl) {
2731
- const authBaseUrl = resolveAuthWebBaseUrl(serverUrl).replace(
2732
- TRAILING_SLASHES2,
2733
- ""
2734
- );
2735
- const canonicalAuthBaseUrl = resolveCanonicalAuthWebBaseUrl(
2736
- serverUrl
2737
- ).replace(TRAILING_SLASHES2, "");
2738
- try {
2739
- return await fetchAuthConfigFromBaseUrl(authBaseUrl);
2740
- } catch (error) {
2741
- if (authBaseUrl !== canonicalAuthBaseUrl && isHostedBraidAuthServer(serverUrl)) {
2742
- return fetchAuthConfigFromBaseUrl(canonicalAuthBaseUrl);
2743
- }
2744
- throw error;
2745
- }
2746
- }
2747
- async function initiateDeviceAuth(convexSiteUrl, deviceInfo) {
2748
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/authorize`;
2749
- const response = await fetch(url, {
2750
- method: "POST",
2751
- headers: { "Content-Type": "application/json" },
2752
- body: JSON.stringify(deviceInfo)
2753
- });
2754
- if (!response.ok) {
2755
- const body = await response.text();
2756
- throw new Error(
2757
- `Failed to initiate device authorization (${response.status}): ${body}`
2758
- );
2759
- }
2760
- return response.json();
2761
- }
2762
- async function pollForSession(convexSiteUrl, deviceCode, interval, expiresIn, timeoutSeconds) {
2763
- const tokenUrl = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/token`;
2764
- const deadline = Date.now() + Math.min(expiresIn, timeoutSeconds) * 1e3;
2765
- let currentInterval = interval;
2766
- while (Date.now() < deadline) {
2767
- await sleep(currentInterval * 1e3);
2768
- const response = await fetch(tokenUrl, {
2769
- method: "POST",
2770
- headers: { "Content-Type": "application/json" },
2771
- body: JSON.stringify({ device_code: deviceCode })
2772
- });
2773
- if (response.ok) {
2774
- const result = await response.json();
2775
- return {
2776
- sessionToken: result.session_token,
2777
- expiresAt: result.expires_at,
2778
- user: result.user
2779
- };
2780
- }
2781
- let errorCode;
2782
- try {
2783
- const errorBody = await response.json();
2784
- errorCode = errorBody.error;
2785
- } catch {
2786
- throw new Error(`Device auth polling error: HTTP ${response.status}`);
2787
- }
2788
- if (errorCode === "authorization_pending") {
2789
- continue;
2790
- }
2791
- if (errorCode === "slow_down") {
2792
- currentInterval += 5;
2793
- continue;
2794
- }
2795
- if (errorCode === "expired_token") {
2796
- throw new DeviceAuthExpiredError();
2797
- }
2798
- if (errorCode === "access_denied") {
2799
- throw new DeviceAuthDeniedError();
2800
- }
2801
- throw new Error(
2802
- `Device auth polling error: ${errorCode ?? `HTTP ${response.status}`}`
2803
- );
2804
- }
2805
- throw new DeviceAuthTimeoutError();
2806
- }
2807
- async function fetchSessionInfo(convexSiteUrl, sessionToken) {
2808
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
2809
- const response = await fetch(url, {
2810
- method: "GET",
2811
- headers: {
2812
- Authorization: `Bearer ${sessionToken}`
2813
- }
2814
- });
2815
- if (response.status === 404) {
2816
- return null;
2817
- }
2818
- if (!response.ok) {
2819
- const body = await response.text();
2820
- throw new Error(
2821
- `Failed to fetch session info (${response.status}): ${body}`
2822
- );
2823
- }
2824
- return response.json();
2825
- }
2826
- async function revokeSession(convexSiteUrl, sessionToken) {
2827
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
2828
- const response = await fetch(url, {
2829
- method: "DELETE",
2830
- headers: {
2831
- Authorization: `Bearer ${sessionToken}`
3228
+ installPath
3229
+ );
3230
+ summary.push({
3231
+ platform: target.id,
3232
+ installPath,
3233
+ written: result.written.length,
3234
+ warnings: result.warnings,
3235
+ errors: result.errors
3236
+ });
2832
3237
  }
2833
- });
2834
- if (response.status === 404) {
2835
- return false;
2836
- }
2837
- if (!response.ok) {
2838
- const body = await response.text();
2839
- throw new Error(`Failed to revoke session (${response.status}): ${body}`);
2840
- }
2841
- return true;
2842
- }
2843
- var noop = () => {
2844
- };
2845
- function openBrowser(url) {
2846
- const currentPlatform = platform();
2847
- if (currentPlatform === "darwin") {
2848
- execFile("open", [url], noop);
2849
- } else if (currentPlatform === "win32") {
2850
- execFile("cmd", ["/c", "start", "", url], noop);
2851
- } else {
2852
- execFile("xdg-open", [url], noop);
3238
+ if (options.json) {
3239
+ writeJson(summary);
3240
+ return;
3241
+ }
3242
+ for (const item of summary) {
3243
+ log.info(
3244
+ `${item.platform}: ${item.written} file(s) written${item.installPath ? ` to ${item.installPath}` : ""}`
3245
+ );
3246
+ for (const warning of item.warnings) {
3247
+ log.warn(` warning: ${warning}`);
3248
+ }
3249
+ for (const error of item.errors) {
3250
+ log.error(` error (${error.agent}): ${error.error}`);
3251
+ }
3252
+ }
3253
+ log.success("sub-agents install completed");
3254
+ } catch (error) {
3255
+ exitCommandError(error, { json: options.json });
2853
3256
  }
2854
3257
  }
2855
- function getDeviceInfo() {
2856
- return {
2857
- deviceName: hostname(),
2858
- deviceOs: platform(),
2859
- deviceHostname: hostname()
2860
- };
2861
- }
2862
3258
 
2863
3259
  // src/commands/auth.ts
3260
+ init_esm_shims();
3261
+ init_api();
3262
+ init_config();
3263
+ init_device_auth();
2864
3264
  init_tui();
2865
- var SESSION_TOKEN_PREFIX = "brs_";
3265
+ import process10 from "process";
3266
+ var SESSION_TOKEN_PREFIX3 = "brs_";
3267
+ var LOOPBACK_HOSTS3 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
2866
3268
  var DEFAULT_TIMEOUT_SECONDS = 300;
3269
+ var resolvePersistedCliServerUrl = (serverUrl, convexSiteUrl) => {
3270
+ try {
3271
+ const convexUrl = new URL(convexSiteUrl);
3272
+ if (LOOPBACK_HOSTS3.has(convexUrl.hostname)) {
3273
+ return convexSiteUrl;
3274
+ }
3275
+ } catch {
3276
+ return serverUrl;
3277
+ }
3278
+ return serverUrl;
3279
+ };
2867
3280
  async function configureDefaultScopeAsync(serverUrl) {
2868
3281
  const shouldConfigureScope = await confirm({
2869
3282
  message: "Configure organization and scope defaults now?",
@@ -2898,9 +3311,9 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2898
3311
  log.error(
2899
3312
  "The API key could not be validated. Please check your key and try again."
2900
3313
  );
2901
- process8.exit(1);
3314
+ process10.exit(1);
2902
3315
  }
2903
- await persistApiKeyAsync(apiKey);
3316
+ await persistApiKeyAsync(apiKey, serverUrl);
2904
3317
  stopAuthSpinner("API key validated and saved");
2905
3318
  if (options.scope !== false) {
2906
3319
  await configureDefaultScopeAsync(serverUrl);
@@ -2913,7 +3326,7 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2913
3326
  stopAuthSpinner("Validation failed");
2914
3327
  const message = error instanceof Error ? error.message : String(error);
2915
3328
  log.error(`Failed to validate API key: ${message}`);
2916
- process8.exit(1);
3329
+ process10.exit(1);
2917
3330
  }
2918
3331
  }
2919
3332
  function handlePollingError(error) {
@@ -2927,7 +3340,7 @@ function handlePollingError(error) {
2927
3340
  const message = error instanceof Error ? error.message : String(error);
2928
3341
  log.error(`Authentication failed: ${message}`);
2929
3342
  }
2930
- process8.exit(1);
3343
+ process10.exit(1);
2931
3344
  }
2932
3345
  async function deviceFlow(serverUrl, timeoutSeconds, options) {
2933
3346
  const configSpinner = spinner();
@@ -2940,7 +3353,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2940
3353
  configSpinner.stop("Failed to fetch auth configuration");
2941
3354
  const message = error instanceof Error ? error.message : String(error);
2942
3355
  log.error(`Could not connect to server: ${message}`);
2943
- process8.exit(1);
3356
+ process10.exit(1);
2944
3357
  }
2945
3358
  const deviceSpinner = spinner();
2946
3359
  deviceSpinner.start("Initiating device authorization...");
@@ -2955,7 +3368,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2955
3368
  deviceSpinner.stop("Failed to initiate device authorization");
2956
3369
  const message = error instanceof Error ? error.message : String(error);
2957
3370
  log.error(`Device authorization failed: ${message}`);
2958
- process8.exit(1);
3371
+ process10.exit(1);
2959
3372
  }
2960
3373
  const verificationUrl = buildAuthVerificationUrlFromBaseUrl(
2961
3374
  authConfig.verificationBaseUrl,
@@ -2989,9 +3402,13 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2989
3402
  pollSpinner.stop("Authentication failed");
2990
3403
  handlePollingError(error);
2991
3404
  }
2992
- await persistApiKeyAsync(session.sessionToken);
3405
+ const persistedServerUrl = resolvePersistedCliServerUrl(
3406
+ serverUrl,
3407
+ authConfig.convexSiteUrl
3408
+ );
3409
+ await persistApiKeyAsync(session.sessionToken, persistedServerUrl);
2993
3410
  if (options.scope !== false) {
2994
- await configureDefaultScopeAsync(authConfig.convexSiteUrl);
3411
+ await configureDefaultScopeAsync(persistedServerUrl);
2995
3412
  }
2996
3413
  log.success(`Session saved to ${CONFIG_FILE}`);
2997
3414
  outro(
@@ -3019,12 +3436,12 @@ async function authCommand(options) {
3019
3436
  const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : DEFAULT_TIMEOUT_SECONDS;
3020
3437
  if (Number.isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
3021
3438
  log.error("Invalid timeout value. Must be a positive number of seconds.");
3022
- process8.exit(1);
3439
+ process10.exit(1);
3023
3440
  }
3024
3441
  await deviceFlow(serverUrl, timeoutSeconds, options);
3025
3442
  }
3026
3443
  async function displayTokenSource(masked) {
3027
- if (process8.env.BRAID_API_KEY) {
3444
+ if (process10.env.BRAID_API_KEY) {
3028
3445
  log.info(`Authenticated with key: ${masked}`);
3029
3446
  log.info("Source: BRAID_API_KEY environment variable");
3030
3447
  return;
@@ -3105,7 +3522,7 @@ async function authStatusCommand() {
3105
3522
  );
3106
3523
  return;
3107
3524
  }
3108
- if (config.token.startsWith(SESSION_TOKEN_PREFIX)) {
3525
+ if (config.token.startsWith(SESSION_TOKEN_PREFIX3)) {
3109
3526
  await displaySessionInfo(config.token);
3110
3527
  await displayProjectConfig();
3111
3528
  displayResolvedSettings(config);
@@ -3128,7 +3545,7 @@ async function revokeSessionIfActive(token) {
3128
3545
  async function authLogoutCommand() {
3129
3546
  const config = await loadMergedConfigAsync();
3130
3547
  const { clearApiKeyAsync: clearApiKeyAsync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3131
- if (config.token?.startsWith(SESSION_TOKEN_PREFIX)) {
3548
+ if (config.token?.startsWith(SESSION_TOKEN_PREFIX3)) {
3132
3549
  await revokeSessionIfActive(config.token);
3133
3550
  }
3134
3551
  await clearApiKeyAsync2();
@@ -3140,7 +3557,6 @@ init_esm_shims();
3140
3557
  init_api();
3141
3558
  init_config();
3142
3559
  init_tui();
3143
- import process9 from "process";
3144
3560
  var parseCsv2 = (input) => {
3145
3561
  if (!input) {
3146
3562
  return void 0;
@@ -3148,15 +3564,6 @@ var parseCsv2 = (input) => {
3148
3564
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3149
3565
  return values.length > 0 ? values : void 0;
3150
3566
  };
3151
- var writeJson2 = (value) => {
3152
- process9.stdout.write(`${JSON.stringify(value, null, 2)}
3153
- `);
3154
- };
3155
- var exitWithError2 = (error) => {
3156
- const message = error instanceof Error ? error.message : String(error);
3157
- log.error(message);
3158
- process9.exit(1);
3159
- };
3160
3567
  var applyCommonOptions = (base, options) => ({
3161
3568
  ...base,
3162
3569
  ...options.server ? { serverUrl: options.server } : {},
@@ -3183,7 +3590,7 @@ async function projectsListCommand(options) {
3183
3590
  applyCommonOptions({}, options)
3184
3591
  );
3185
3592
  if (options.json) {
3186
- writeJson2(result);
3593
+ writeJson(result);
3187
3594
  return;
3188
3595
  }
3189
3596
  log.info("Personal projects:");
@@ -3205,7 +3612,7 @@ Organization: ${orgProjects.orgName} (${orgProjects.orgId})`);
3205
3612
  }
3206
3613
  }
3207
3614
  } catch (error) {
3208
- exitWithError2(error);
3615
+ exitCommandError(error, { json: options.json });
3209
3616
  }
3210
3617
  }
3211
3618
  var buildRulesRequest = async (options) => {
@@ -3226,7 +3633,7 @@ async function rulesListCommand(options) {
3226
3633
  await buildRulesRequest(options)
3227
3634
  );
3228
3635
  if (options.json) {
3229
- writeJson2(result);
3636
+ writeJson(result);
3230
3637
  return;
3231
3638
  }
3232
3639
  if (result.rules.length === 0) {
@@ -3237,7 +3644,7 @@ async function rulesListCommand(options) {
3237
3644
  log.info(`${rule.id} ${rule.title}`);
3238
3645
  }
3239
3646
  } catch (error) {
3240
- exitWithError2(error);
3647
+ exitCommandError(error, { json: options.json });
3241
3648
  }
3242
3649
  }
3243
3650
  var buildSkillsRequest = async (options) => {
@@ -3257,7 +3664,7 @@ async function skillsListCommand(options) {
3257
3664
  try {
3258
3665
  const result = await fetchSkillsAsync(await buildSkillsRequest(options));
3259
3666
  if (options.json) {
3260
- writeJson2(result);
3667
+ writeJson(result);
3261
3668
  return;
3262
3669
  }
3263
3670
  if (result.skills.length === 0) {
@@ -3268,7 +3675,7 @@ async function skillsListCommand(options) {
3268
3675
  log.info(`${skill.name}`);
3269
3676
  }
3270
3677
  } catch (error) {
3271
- exitWithError2(error);
3678
+ exitCommandError(error, { json: options.json });
3272
3679
  }
3273
3680
  }
3274
3681
 
@@ -3415,11 +3822,11 @@ init_lockfile();
3415
3822
  init_esm_shims();
3416
3823
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3417
3824
  import { dirname as dirname6, join as join7 } from "path";
3418
- import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
3825
+ import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3419
3826
  var METADATA_FILENAME2 = ".braid-metadata.json";
3420
- var MetadataReadError = class extends Data5.TaggedError("MetadataReadError") {
3827
+ var MetadataReadError = class extends Data6.TaggedError("MetadataReadError") {
3421
3828
  };
3422
- var MetadataWriteError = class extends Data5.TaggedError("MetadataWriteError") {
3829
+ var MetadataWriteError = class extends Data6.TaggedError("MetadataWriteError") {
3423
3830
  };
3424
3831
  var normalizeInstalledSkill = (skill) => ({
3425
3832
  kind: skill.kind ?? "skill",
@@ -3435,18 +3842,18 @@ var normalizeMetadata = (metadata) => ({
3435
3842
  var getMetadataPath = (skillsDir) => join7(dirname6(skillsDir), METADATA_FILENAME2);
3436
3843
  var readMetadata = (skillsDir) => {
3437
3844
  const metadataPath = getMetadataPath(skillsDir);
3438
- return pipe6(
3439
- Effect6.tryPromise({
3845
+ return pipe7(
3846
+ Effect7.tryPromise({
3440
3847
  try: () => readFile4(metadataPath, "utf-8"),
3441
3848
  catch: (e) => new MetadataReadError({ path: metadataPath, cause: e })
3442
3849
  }),
3443
- Effect6.flatMap(
3444
- (content) => Effect6.try({
3850
+ Effect7.flatMap(
3851
+ (content) => Effect7.try({
3445
3852
  try: () => normalizeMetadata(JSON.parse(content)),
3446
3853
  catch: () => normalizeMetadata(void 0)
3447
3854
  })
3448
3855
  ),
3449
- Effect6.orElseSucceed(() => normalizeMetadata(void 0))
3856
+ Effect7.orElseSucceed(() => normalizeMetadata(void 0))
3450
3857
  );
3451
3858
  };
3452
3859
  var readRawJson = async (path2) => {
@@ -3463,7 +3870,7 @@ var readRawJson = async (path2) => {
3463
3870
  };
3464
3871
  var writeMetadata = (skillsDir, metadata) => {
3465
3872
  const metadataPath = getMetadataPath(skillsDir);
3466
- return Effect6.tryPromise({
3873
+ return Effect7.tryPromise({
3467
3874
  try: async () => {
3468
3875
  const existing = await readRawJson(metadataPath);
3469
3876
  const merged = { ...existing, skills: metadata.skills };
@@ -3472,9 +3879,9 @@ var writeMetadata = (skillsDir, metadata) => {
3472
3879
  catch: (e) => new MetadataWriteError({ path: metadataPath, cause: e })
3473
3880
  });
3474
3881
  };
3475
- var updateMetadata = (skillsDir, newSkills) => pipe6(
3882
+ var updateMetadata = (skillsDir, newSkills) => pipe7(
3476
3883
  readMetadata(skillsDir),
3477
- Effect6.map((existing) => {
3884
+ Effect7.map((existing) => {
3478
3885
  const now = (/* @__PURE__ */ new Date()).toISOString();
3479
3886
  const updatedSkills = [...existing.skills];
3480
3887
  for (const skill of newSkills) {
@@ -3497,35 +3904,35 @@ var updateMetadata = (skillsDir, newSkills) => pipe6(
3497
3904
  }
3498
3905
  return { skills: updatedSkills };
3499
3906
  }),
3500
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3907
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3501
3908
  );
3502
- var removeFromMetadata = (skillsDir, skillName) => pipe6(
3909
+ var removeFromMetadata = (skillsDir, skillName) => pipe7(
3503
3910
  readMetadata(skillsDir),
3504
- Effect6.map((metadata) => ({
3911
+ Effect7.map((metadata) => ({
3505
3912
  skills: metadata.skills.filter((s) => s.name !== skillName)
3506
3913
  })),
3507
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3914
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3508
3915
  );
3509
- var readMetadataAsync = (skillsDir) => Effect6.runPromise(readMetadata(skillsDir));
3510
- var updateMetadataAsync = (skillsDir, newSkills) => Effect6.runPromise(updateMetadata(skillsDir, newSkills));
3511
- var removeFromMetadataAsync = (skillsDir, skillName) => Effect6.runPromise(removeFromMetadata(skillsDir, skillName));
3916
+ var readMetadataAsync = (skillsDir) => Effect7.runPromise(readMetadata(skillsDir));
3917
+ var updateMetadataAsync = (skillsDir, newSkills) => Effect7.runPromise(updateMetadata(skillsDir, newSkills));
3918
+ var removeFromMetadataAsync = (skillsDir, skillName) => Effect7.runPromise(removeFromMetadata(skillsDir, skillName));
3512
3919
 
3513
3920
  // src/lib/rule-writer.ts
3514
3921
  init_esm_shims();
3515
3922
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
3516
3923
  import { dirname as dirname7, resolve as resolve3, sep as sep2 } from "path";
3517
- import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3518
- var RuleWriteError = class extends Data6.TaggedError("RuleWriteError") {
3924
+ import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3925
+ var RuleWriteError = class extends Data7.TaggedError("RuleWriteError") {
3519
3926
  };
3520
- var createDirectory = (dir) => Effect7.tryPromise({
3927
+ var createDirectory = (dir) => Effect8.tryPromise({
3521
3928
  try: () => mkdir4(dir, { recursive: true }),
3522
3929
  catch: (e) => new RuleWriteError({ path: dir, operation: "mkdir", cause: e })
3523
3930
  });
3524
- var writeTextFile = (fullPath, content) => Effect7.tryPromise({
3931
+ var writeTextFile = (fullPath, content) => Effect8.tryPromise({
3525
3932
  try: () => writeFile6(fullPath, content, "utf-8"),
3526
3933
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "write", cause: e })
3527
3934
  });
3528
- var readTextFile = (fullPath) => Effect7.tryPromise({
3935
+ var readTextFile = (fullPath) => Effect8.tryPromise({
3529
3936
  try: () => readFile5(fullPath, "utf-8"),
3530
3937
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "read", cause: e })
3531
3938
  });
@@ -3533,7 +3940,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3533
3940
  const resolvedBase = resolve3(basePath);
3534
3941
  const resolvedFull = resolve3(basePath, ruleName);
3535
3942
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep2)) {
3536
- return Effect7.fail(
3943
+ return Effect8.fail(
3537
3944
  new RuleWriteError({
3538
3945
  path: ruleName,
3539
3946
  operation: "write",
@@ -3543,7 +3950,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3543
3950
  })
3544
3951
  );
3545
3952
  }
3546
- return Effect7.succeed(resolvedFull);
3953
+ return Effect8.succeed(resolvedFull);
3547
3954
  };
3548
3955
  var BRAID_SECTION_START = "<!-- braid:rules:start -->";
3549
3956
  var BRAID_SECTION_END = "<!-- braid:rules:end -->";
@@ -3576,30 +3983,30 @@ function buildAppendContent(rules2) {
3576
3983
  lines.push(BRAID_SECTION_END);
3577
3984
  return lines.join("\n");
3578
3985
  }
3579
- var writeMdcRules = (basePath, rules2) => pipe7(
3986
+ var writeMdcRules = (basePath, rules2) => pipe8(
3580
3987
  createDirectory(basePath),
3581
- Effect7.flatMap(
3582
- () => Effect7.forEach(
3988
+ Effect8.flatMap(
3989
+ () => Effect8.forEach(
3583
3990
  rules2,
3584
- (rule) => pipe7(
3991
+ (rule) => pipe8(
3585
3992
  assertRulePathWithinBase(basePath, `${rule.name}.mdc`),
3586
- Effect7.flatMap(
3993
+ Effect8.flatMap(
3587
3994
  (filePath) => writeTextFile(filePath, buildMdcContent(rule))
3588
3995
  )
3589
3996
  ),
3590
3997
  { concurrency: "unbounded" }
3591
3998
  )
3592
3999
  ),
3593
- Effect7.asVoid
4000
+ Effect8.asVoid
3594
4001
  );
3595
- var writeMarkdownDirRules = (basePath, rules2) => pipe7(
4002
+ var writeMarkdownDirRules = (basePath, rules2) => pipe8(
3596
4003
  createDirectory(basePath),
3597
- Effect7.flatMap(
3598
- () => Effect7.forEach(
4004
+ Effect8.flatMap(
4005
+ () => Effect8.forEach(
3599
4006
  rules2,
3600
- (rule) => pipe7(
4007
+ (rule) => pipe8(
3601
4008
  assertRulePathWithinBase(basePath, `${rule.name}.md`),
3602
- Effect7.flatMap(
4009
+ Effect8.flatMap(
3603
4010
  (filePath) => writeTextFile(filePath, `# ${rule.title}
3604
4011
 
3605
4012
  ${rule.content}`)
@@ -3608,17 +4015,17 @@ ${rule.content}`)
3608
4015
  { concurrency: "unbounded" }
3609
4016
  )
3610
4017
  ),
3611
- Effect7.asVoid
4018
+ Effect8.asVoid
3612
4019
  );
3613
- var writeAppendSingleRules = (filePath, rules2) => pipe7(
4020
+ var writeAppendSingleRules = (filePath, rules2) => pipe8(
3614
4021
  createDirectory(dirname7(filePath)),
3615
- Effect7.flatMap(
3616
- () => pipe7(
4022
+ Effect8.flatMap(
4023
+ () => pipe8(
3617
4024
  readTextFile(filePath),
3618
- Effect7.orElseSucceed(() => "")
4025
+ Effect8.orElseSucceed(() => "")
3619
4026
  )
3620
4027
  ),
3621
- Effect7.flatMap((existing) => {
4028
+ Effect8.flatMap((existing) => {
3622
4029
  const braidContent = buildAppendContent(rules2);
3623
4030
  const startIdx = existing.indexOf(BRAID_SECTION_START);
3624
4031
  const endIdx = existing.indexOf(BRAID_SECTION_END);
@@ -3642,21 +4049,21 @@ var writeRulesForFormat = (basePath, rules2, format) => {
3642
4049
  case "append-single":
3643
4050
  return writeAppendSingleRules(basePath, rules2);
3644
4051
  default:
3645
- return Effect7.void;
4052
+ return Effect8.void;
3646
4053
  }
3647
4054
  };
3648
4055
  var writeRulesForAgent = (agent, rules2, rulesPath) => {
3649
4056
  if (!agent.ruleFormat || rules2.length === 0) {
3650
- return Effect7.succeed({ written: 0, errors: [] });
4057
+ return Effect8.succeed({ written: 0, errors: [] });
3651
4058
  }
3652
- return pipe7(
4059
+ return pipe8(
3653
4060
  writeRulesForFormat(rulesPath, rules2, agent.ruleFormat),
3654
- Effect7.map(() => ({
4061
+ Effect8.map(() => ({
3655
4062
  written: rules2.length,
3656
4063
  errors: []
3657
4064
  })),
3658
- Effect7.catch(
3659
- (error) => Effect7.succeed({
4065
+ Effect8.catch(
4066
+ (error) => Effect8.succeed({
3660
4067
  written: 0,
3661
4068
  errors: [
3662
4069
  {
@@ -3668,29 +4075,29 @@ var writeRulesForAgent = (agent, rules2, rulesPath) => {
3668
4075
  )
3669
4076
  );
3670
4077
  };
3671
- var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect7.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
4078
+ var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect8.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
3672
4079
 
3673
4080
  // src/lib/skill-writer.ts
3674
4081
  init_esm_shims();
3675
4082
  import { createHash as createHash2 } from "crypto";
3676
4083
  import { chmod, mkdir as mkdir5, rm as rm2, symlink, writeFile as writeFile7 } from "fs/promises";
3677
4084
  import { dirname as dirname8, join as join8, resolve as resolve4, sep as sep3 } from "path";
3678
- import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3679
- var WriteError = class extends Data7.TaggedError("WriteError") {
4085
+ import { Data as Data8, Effect as Effect9, pipe as pipe9 } from "effect";
4086
+ var WriteError = class extends Data8.TaggedError("WriteError") {
3680
4087
  };
3681
- var createDirectory2 = (dir, fullPath) => Effect8.tryPromise({
4088
+ var createDirectory2 = (dir, fullPath) => Effect9.tryPromise({
3682
4089
  try: () => mkdir5(dir, { recursive: true }),
3683
4090
  catch: (e) => new WriteError({ path: fullPath, operation: "mkdir", cause: e })
3684
4091
  });
3685
- var writeTextFile2 = (fullPath, content) => Effect8.tryPromise({
4092
+ var writeTextFile2 = (fullPath, content) => Effect9.tryPromise({
3686
4093
  try: () => writeFile7(fullPath, content, "utf-8"),
3687
4094
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3688
4095
  });
3689
- var writeBinaryFile = (fullPath, content) => Effect8.tryPromise({
4096
+ var writeBinaryFile = (fullPath, content) => Effect9.tryPromise({
3690
4097
  try: () => writeFile7(fullPath, content),
3691
4098
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3692
4099
  });
3693
- var makeExecutable = (fullPath) => Effect8.tryPromise({
4100
+ var makeExecutable = (fullPath) => Effect9.tryPromise({
3694
4101
  try: () => chmod(fullPath, 493),
3695
4102
  catch: (e) => new WriteError({ path: fullPath, operation: "chmod", cause: e })
3696
4103
  });
@@ -3698,7 +4105,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3698
4105
  const resolvedBase = resolve4(basePath);
3699
4106
  const resolvedFull = resolve4(basePath, untrustedPath);
3700
4107
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep3)) {
3701
- return Effect8.fail(
4108
+ return Effect9.fail(
3702
4109
  new WriteError({
3703
4110
  path: untrustedPath,
3704
4111
  operation: "write",
@@ -3708,7 +4115,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3708
4115
  })
3709
4116
  );
3710
4117
  }
3711
- return Effect8.succeed(resolvedFull);
4118
+ return Effect9.succeed(resolvedFull);
3712
4119
  };
3713
4120
  var COMPATIBILITY_REGEX = /^compatibility:\s*.+$/m;
3714
4121
  var rewriteCompatibility = (content, agentId) => {
@@ -3751,22 +4158,22 @@ var isScriptFile = (path2) => {
3751
4158
  return inScriptsDir && scriptExtensions.some((ext) => lowerPath.endsWith(ext));
3752
4159
  };
3753
4160
  var writeFileContent = (fullPath, file, agentId) => isBinaryFile(file.path) ? writeBinaryFile(fullPath, decodeFileContentBinary(file)) : writeTextFile2(fullPath, decodeFileContent(file, agentId));
3754
- var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path) && decodeFileContent(file, agentId).startsWith("#!") ? makeExecutable(fullPath) : Effect8.void;
3755
- var writeSkillFile = (basePath, file, agentId) => pipe8(
4161
+ var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path) && decodeFileContent(file, agentId).startsWith("#!") ? makeExecutable(fullPath) : Effect9.void;
4162
+ var writeSkillFile = (basePath, file, agentId) => pipe9(
3756
4163
  assertWithinBase2(basePath, file.path),
3757
- Effect8.flatMap((fullPath) => {
4164
+ Effect9.flatMap((fullPath) => {
3758
4165
  const dir = dirname8(fullPath);
3759
- return pipe8(
4166
+ return pipe9(
3760
4167
  createDirectory2(dir, fullPath),
3761
- Effect8.flatMap(() => writeFileContent(fullPath, file, agentId)),
3762
- Effect8.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
4168
+ Effect9.flatMap(() => writeFileContent(fullPath, file, agentId)),
4169
+ Effect9.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
3763
4170
  );
3764
4171
  })
3765
4172
  );
3766
- var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
4173
+ var writeSkillAtPath = (skillPath, skill, agentId) => pipe9(
3767
4174
  createDirectory2(skillPath, skillPath),
3768
- Effect8.flatMap(
3769
- () => Effect8.forEach(
4175
+ Effect9.flatMap(
4176
+ () => Effect9.forEach(
3770
4177
  skill.files,
3771
4178
  (file) => writeSkillFile(skillPath, file, agentId),
3772
4179
  {
@@ -3774,16 +4181,16 @@ var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
3774
4181
  }
3775
4182
  )
3776
4183
  ),
3777
- Effect8.map(() => void 0)
4184
+ Effect9.map(() => void 0)
3778
4185
  );
3779
- var writeSkill = (basePath, skill, agentId) => pipe8(
4186
+ var writeSkill = (basePath, skill, agentId) => pipe9(
3780
4187
  assertWithinBase2(basePath, skill.name),
3781
- Effect8.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
4188
+ Effect9.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
3782
4189
  );
3783
- var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
4190
+ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe9(
3784
4191
  assertWithinBase2(basePath, skill.name),
3785
- Effect8.flatMap(
3786
- (installPath) => Effect8.tryPromise({
4192
+ Effect9.flatMap(
4193
+ (installPath) => Effect9.tryPromise({
3787
4194
  try: async () => {
3788
4195
  if (!options.storeRoot) {
3789
4196
  throw new Error("storeRoot is required for shared-store installs");
@@ -3793,7 +4200,7 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3793
4200
  skill,
3794
4201
  agentId
3795
4202
  );
3796
- await Effect8.runPromise(
4203
+ await Effect9.runPromise(
3797
4204
  writeSkillAtPath(storedBundlePath, skill, agentId)
3798
4205
  );
3799
4206
  const destinationPath = await resolveManagedBundlePathAsync(
@@ -3820,14 +4227,14 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3820
4227
  );
3821
4228
  var hashSkill = (skill, agentId) => createHash2("sha256").update(JSON.stringify({ agentId, skill })).digest("hex").slice(0, 16);
3822
4229
  var resolveStorePath = (storeRoot, skill, agentId) => join8(storeRoot, `${skill.name}-${hashSkill(skill, agentId)}`);
3823
- var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3824
- Effect8.forEach(
4230
+ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe9(
4231
+ Effect9.forEach(
3825
4232
  skills2,
3826
- (skill) => pipe8(
4233
+ (skill) => pipe9(
3827
4234
  options.storeRoot ? writeSkillSymlink(basePath, skill, agentId, options) : writeSkill(basePath, skill, agentId),
3828
- Effect8.map(() => ({ success: true, skill: skill.name })),
3829
- Effect8.catch(
3830
- (error) => Effect8.succeed({
4235
+ Effect9.map(() => ({ success: true, skill: skill.name })),
4236
+ Effect9.catch(
4237
+ (error) => Effect9.succeed({
3831
4238
  success: false,
3832
4239
  skill: skill.name,
3833
4240
  error: error.cause instanceof Error ? error.cause.message : String(error.cause)
@@ -3836,14 +4243,14 @@ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3836
4243
  ),
3837
4244
  { concurrency: "unbounded" }
3838
4245
  ),
3839
- Effect8.map((results) => ({
4246
+ Effect9.map((results) => ({
3840
4247
  written: results.filter((r) => r.success).map((r) => r.skill),
3841
4248
  errors: results.filter(
3842
4249
  (r) => !r.success
3843
4250
  ).map((r) => ({ skill: r.skill, error: r.error }))
3844
4251
  }))
3845
4252
  );
3846
- var writeSkillsAsync = (basePath, skills2, agentId, options) => Effect8.runPromise(writeSkills(basePath, skills2, agentId, options));
4253
+ var writeSkillsAsync = (basePath, skills2, agentId, options) => Effect9.runPromise(writeSkills(basePath, skills2, agentId, options));
3847
4254
 
3848
4255
  // src/commands/install.ts
3849
4256
  init_tui();
@@ -4293,6 +4700,7 @@ async function writeLockfileAfterInstall(handle, slug, version, contentHash, rul
4293
4700
  }
4294
4701
  }
4295
4702
  async function publicInstallCommand(source, options) {
4703
+ const config = await loadMergedConfigAsync();
4296
4704
  const parsed = parsePublicSource(source);
4297
4705
  if (!parsed) {
4298
4706
  log.error(`Invalid public source: ${source}`);
@@ -4316,6 +4724,7 @@ async function publicInstallCommand(source, options) {
4316
4724
  log.info(`Using locked version: v${version}`);
4317
4725
  }
4318
4726
  const versionSuffix = version !== void 0 ? `@${version}` : "";
4727
+ const serverUrl = options.server ?? config.serverUrl;
4319
4728
  intro(`Installing from @${handle}/${slug}${versionSuffix}`);
4320
4729
  const installSpinner = spinner();
4321
4730
  installSpinner.start("Fetching public content...");
@@ -4325,7 +4734,10 @@ async function publicInstallCommand(source, options) {
4325
4734
  slug,
4326
4735
  version,
4327
4736
  lockedRuleIds,
4328
- options,
4737
+ {
4738
+ ...options,
4739
+ server: serverUrl
4740
+ },
4329
4741
  installSpinner
4330
4742
  );
4331
4743
  installSpinner.stop(`Downloaded ${summarizeContent(response)}`);
@@ -4333,11 +4745,13 @@ async function publicInstallCommand(source, options) {
4333
4745
  log.warn("No content to install.");
4334
4746
  process.exit(0);
4335
4747
  }
4336
- const serverUrl = options.server ?? "https://braid.cloud";
4337
4748
  const { totalWritten, totalErrors } = await installToSelectedAgents(
4338
4749
  response,
4339
4750
  serverUrl,
4340
- options,
4751
+ {
4752
+ ...options,
4753
+ server: serverUrl
4754
+ },
4341
4755
  installSpinner
4342
4756
  );
4343
4757
  if (totalWritten > 0 && version !== void 0) {
@@ -4534,7 +4948,8 @@ async function listCommand(options) {
4534
4948
 
4535
4949
  // src/commands/manage.ts
4536
4950
  init_esm_shims();
4537
- import process10 from "process";
4951
+ init_device_auth();
4952
+ import process11 from "process";
4538
4953
 
4539
4954
  // src/lib/manage-server.ts
4540
4955
  init_esm_shims();
@@ -4544,6 +4959,7 @@ import { createServer } from "http";
4544
4959
  import { isAbsolute, relative as relative2, resolve as resolve6 } from "path";
4545
4960
  import { fileURLToPath as fileURLToPath2 } from "url";
4546
4961
  init_config();
4962
+ init_device_auth();
4547
4963
 
4548
4964
  // src/lib/manage-actions.ts
4549
4965
  init_esm_shims();
@@ -4554,8 +4970,8 @@ import { basename as basename2, dirname as dirname10, join as join11, relative }
4554
4970
  init_esm_shims();
4555
4971
  import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile8 } from "fs/promises";
4556
4972
  import { dirname as dirname9, join as join9 } from "path";
4557
- import { Data as Data8, Effect as Effect9 } from "effect";
4558
- var HookConfigWriteError = class extends Data8.TaggedError("HookConfigWriteError") {
4973
+ import { Data as Data9, Effect as Effect10 } from "effect";
4974
+ var HookConfigWriteError = class extends Data9.TaggedError("HookConfigWriteError") {
4559
4975
  };
4560
4976
  var HOOK_METADATA_FILENAME = ".braid-metadata.json";
4561
4977
  function stableStringify(value) {
@@ -4880,7 +5296,7 @@ async function ensureMarketplaceHookBundleFile(settingsPath, sourceSlug) {
4880
5296
  });
4881
5297
  return bundlePath;
4882
5298
  }
4883
- var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise({
5299
+ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect10.tryPromise({
4884
5300
  try: async () => {
4885
5301
  const activeHooks = hooks.filter(
4886
5302
  (hook) => hook.enabled !== false && hook.runtime === "claude"
@@ -4935,131 +5351,18 @@ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise
4935
5351
  },
4936
5352
  catch: (cause) => new HookConfigWriteError({ path: settingsPath, cause })
4937
5353
  });
4938
- var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect9.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
5354
+ var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect10.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
4939
5355
  var removeMarketplaceHooksBySourceAsync = (settingsPath, sourceSlug) => removeMarketplaceHooksBySource(settingsPath, sourceSlug);
4940
5356
  var ensureMarketplaceHookBundleFileAsync = (settingsPath, sourceSlug) => ensureMarketplaceHookBundleFile(settingsPath, sourceSlug);
4941
5357
  var listInstalledMarketplaceHookSourcesAsync = (settingsPath) => listInstalledMarketplaceHookSources(settingsPath);
4942
5358
  var readMarketplaceHookBundleFileAsync = (bundlePath) => readMarketplaceHookBundleFile(bundlePath);
4943
5359
 
4944
- // src/lib/marketplace-api.ts
4945
- init_esm_shims();
4946
- init_config();
4947
- import { Data as Data9, Effect as Effect10, pipe as pipe9 } from "effect";
4948
- var TRAILING_SLASH_REGEX2 = /\/$/;
4949
- var MarketplaceApiError = class extends Data9.TaggedError("MarketplaceApiError") {
4950
- };
4951
- var resolveApiKey2 = (provided) => {
4952
- if (provided) {
4953
- return Effect10.succeed(provided);
4954
- }
4955
- return pipe9(
4956
- Effect10.tryPromise({
4957
- try: () => getApiKeyAsync(),
4958
- catch: () => new MarketplaceApiError({
4959
- message: "Failed to load API key",
4960
- status: 500,
4961
- code: "CONFIG_ERROR"
4962
- })
4963
- }),
4964
- Effect10.flatMap(
4965
- (token) => token ? Effect10.succeed(token) : Effect10.fail(
4966
- new MarketplaceApiError({
4967
- message: "Not signed in. Run 'braid auth'.",
4968
- status: 401,
4969
- code: "AUTH_REQUIRED"
4970
- })
4971
- )
4972
- )
4973
- );
4974
- };
4975
- var resolveServer = (provided) => {
4976
- if (provided) {
4977
- return Effect10.succeed(provided.replace(TRAILING_SLASH_REGEX2, ""));
4978
- }
4979
- return pipe9(
4980
- Effect10.tryPromise({
4981
- try: () => getServerUrlAsync(),
4982
- catch: () => new MarketplaceApiError({
4983
- message: "Failed to load server URL",
4984
- status: 500,
4985
- code: "CONFIG_ERROR"
4986
- })
4987
- }),
4988
- Effect10.map((server) => server.replace(TRAILING_SLASH_REGEX2, ""))
4989
- );
4990
- };
4991
- var requestJson = (url, options) => pipe9(
4992
- Effect10.tryPromise({
4993
- try: () => fetch(url, options),
4994
- catch: () => new MarketplaceApiError({
4995
- message: "Network error",
4996
- status: 503,
4997
- code: "NETWORK_ERROR"
4998
- })
4999
- }),
5000
- Effect10.flatMap(
5001
- (response) => Effect10.tryPromise({
5002
- try: async () => {
5003
- const payload = await response.json().catch(() => null);
5004
- if (!response.ok) {
5005
- throw new MarketplaceApiError({
5006
- message: payload?.error ?? payload?.message ?? "Request failed",
5007
- status: response.status,
5008
- code: payload?.code ?? "REQUEST_ERROR"
5009
- });
5010
- }
5011
- return payload;
5012
- },
5013
- catch: (error) => error instanceof MarketplaceApiError ? error : new MarketplaceApiError({
5014
- message: "Invalid response",
5015
- status: 500,
5016
- code: "PARSE_ERROR"
5017
- })
5018
- })
5019
- )
5020
- );
5021
- var fetchMarketplaceLibrary = (options) => pipe9(
5022
- Effect10.all({
5023
- server: resolveServer(options.server),
5024
- apiKey: resolveApiKey2(options.apiKey)
5025
- }),
5026
- Effect10.flatMap(
5027
- ({ server, apiKey }) => requestJson(
5028
- `${server}/api/marketplace/library`,
5029
- {
5030
- method: "GET",
5031
- headers: {
5032
- Authorization: `Bearer ${apiKey}`
5033
- }
5034
- }
5035
- )
5036
- ),
5037
- Effect10.map((response) => response.items)
5038
- );
5039
- var fetchMarketplaceInstallManifest = (slug, options) => pipe9(
5040
- Effect10.all({
5041
- server: resolveServer(options.server),
5042
- apiKey: resolveApiKey2(options.apiKey)
5043
- }),
5044
- Effect10.flatMap(
5045
- ({ server, apiKey }) => requestJson(
5046
- `${server}/api/marketplace/install-manifest/${encodeURIComponent(slug)}`,
5047
- {
5048
- method: "GET",
5049
- headers: {
5050
- Authorization: `Bearer ${apiKey}`
5051
- }
5052
- }
5053
- )
5054
- )
5055
- );
5056
- var fetchMarketplaceLibraryAsync = (options) => Effect10.runPromise(fetchMarketplaceLibrary(options));
5057
- var fetchMarketplaceInstallManifestAsync = (slug, options) => Effect10.runPromise(fetchMarketplaceInstallManifest(slug, options));
5058
-
5059
5360
  // src/lib/marketplace-installer.ts
5060
5361
  init_esm_shims();
5061
5362
  import { rm as rm4 } from "fs/promises";
5062
5363
  import { join as join10 } from "path";
5364
+ init_config();
5365
+ var TRAILING_SLASH_REGEX3 = /\/$/;
5063
5366
  function parseAgentIds(value) {
5064
5367
  if (!value) {
5065
5368
  return [];
@@ -5129,7 +5432,7 @@ async function installSkillsForAgent(args) {
5129
5432
  versionId: args.manifest.versionId
5130
5433
  },
5131
5434
  version: args.manifest.versionId ?? args.manifest.commitSha ?? "unknown",
5132
- serverUrl: "https://braid.cloud"
5435
+ serverUrl: args.serverUrl
5133
5436
  }))
5134
5437
  );
5135
5438
  args.targets.push(`${args.agent.name} -> ${installPath}`);
@@ -5218,6 +5521,7 @@ async function installAllForAgent(args) {
5218
5521
  agent: args.agent,
5219
5522
  manifest: args.manifest,
5220
5523
  slug: args.slug,
5524
+ serverUrl: args.serverUrl,
5221
5525
  skills: args.skills,
5222
5526
  global: args.global,
5223
5527
  installedSkills: args.installedSkills,
@@ -5275,6 +5579,7 @@ async function installMarketplaceSkillSet(options) {
5275
5579
  "No compatible agents found. Use --agents to specify targets."
5276
5580
  );
5277
5581
  }
5582
+ const serverUrl = (options.serverUrl ?? await getServerUrlAsync().catch(() => "https://braid.cloud")).replace(TRAILING_SLASH_REGEX3, "");
5278
5583
  const targets = [];
5279
5584
  const installedSkills = /* @__PURE__ */ new Set();
5280
5585
  for (const agent of targetAgents) {
@@ -5282,6 +5587,7 @@ async function installMarketplaceSkillSet(options) {
5282
5587
  agent,
5283
5588
  manifest: options.manifest,
5284
5589
  slug: options.slug,
5590
+ serverUrl,
5285
5591
  skills: resolvedSkills,
5286
5592
  rules: rules2,
5287
5593
  agents: agents2,
@@ -5431,7 +5737,8 @@ async function installLibraryPackAsync(options) {
5431
5737
  agents: options.agentId,
5432
5738
  global: options.scope === "global",
5433
5739
  manifest,
5434
- slug: options.slug
5740
+ slug: options.slug,
5741
+ serverUrl: options.server
5435
5742
  });
5436
5743
  }
5437
5744
 
@@ -5908,7 +6215,8 @@ var MIME_TYPES = {
5908
6215
  };
5909
6216
  var TRAILING_SLASHES3 = /\/+$/;
5910
6217
  var MANAGE_AUTH_TIMEOUT_SECONDS = 300;
5911
- var SESSION_TOKEN_PREFIX2 = "brs_";
6218
+ var SESSION_TOKEN_PREFIX4 = "brs_";
6219
+ var LOOPBACK_HOSTS4 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
5912
6220
  function getErrorStatus(error) {
5913
6221
  if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") {
5914
6222
  return error.status ?? 500;
@@ -5985,7 +6293,18 @@ function getContentType(pathname) {
5985
6293
  const extension = pathname.slice(pathname.lastIndexOf("."));
5986
6294
  return MIME_TYPES[extension] ?? "application/octet-stream";
5987
6295
  }
5988
- function writeJson3(response, status, body) {
6296
+ function resolvePersistedCliServerUrl2(serverUrl, convexSiteUrl) {
6297
+ try {
6298
+ const convexUrl = new URL(convexSiteUrl);
6299
+ if (LOOPBACK_HOSTS4.has(convexUrl.hostname)) {
6300
+ return convexSiteUrl;
6301
+ }
6302
+ } catch {
6303
+ return serverUrl;
6304
+ }
6305
+ return serverUrl;
6306
+ }
6307
+ function writeJson2(response, status, body) {
5989
6308
  response.writeHead(status, {
5990
6309
  "content-type": "application/json; charset=utf-8",
5991
6310
  "cache-control": "no-store"
@@ -6009,7 +6328,7 @@ async function serveStaticAsset(serverResponse, assetRoot, pathname) {
6009
6328
  const assetPath = resolve6(assetRoot, relativePath);
6010
6329
  const resolvedRoot = resolve6(assetRoot);
6011
6330
  if (!isWithinManageAssetRoot(resolvedRoot, assetPath)) {
6012
- writeJson3(serverResponse, 404, { error: "Not found" });
6331
+ writeJson2(serverResponse, 404, { error: "Not found" });
6013
6332
  return true;
6014
6333
  }
6015
6334
  try {
@@ -6103,19 +6422,19 @@ async function handleManageApiRoute(request, response, dependencies) {
6103
6422
  const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
6104
6423
  switch (`${method} ${pathname}`) {
6105
6424
  case "GET /api/inventory":
6106
- writeJson3(response, 200, await dependencies.getInventory());
6425
+ writeJson2(response, 200, await dependencies.getInventory());
6107
6426
  return true;
6108
6427
  case "GET /api/library":
6109
- writeJson3(response, 200, await dependencies.listLibrary());
6428
+ writeJson2(response, 200, await dependencies.listLibrary());
6110
6429
  return true;
6111
6430
  case "GET /api/auth/session":
6112
- writeJson3(response, 200, {
6431
+ writeJson2(response, 200, {
6113
6432
  ok: true,
6114
6433
  result: await dependencies.getAuthSession()
6115
6434
  });
6116
6435
  return true;
6117
6436
  case "POST /api/auth/start":
6118
- writeJson3(response, 200, {
6437
+ writeJson2(response, 200, {
6119
6438
  ok: true,
6120
6439
  result: await dependencies.startAuth()
6121
6440
  });
@@ -6129,7 +6448,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6129
6448
  "AUTH_REQUEST_REQUIRED"
6130
6449
  );
6131
6450
  }
6132
- writeJson3(response, 200, {
6451
+ writeJson2(response, 200, {
6133
6452
  ok: true,
6134
6453
  result: await dependencies.completeAuth({
6135
6454
  requestId: payload.requestId
@@ -6138,14 +6457,14 @@ async function handleManageApiRoute(request, response, dependencies) {
6138
6457
  return true;
6139
6458
  }
6140
6459
  case "POST /api/auth/sign-out":
6141
- writeJson3(response, 200, {
6460
+ writeJson2(response, 200, {
6142
6461
  ok: true,
6143
6462
  result: await dependencies.signOut()
6144
6463
  });
6145
6464
  return true;
6146
6465
  case "POST /api/actions/install": {
6147
6466
  const payload = await readJsonBody(request);
6148
- writeJson3(response, 200, {
6467
+ writeJson2(response, 200, {
6149
6468
  ok: true,
6150
6469
  result: await dependencies.installLibraryPack(payload)
6151
6470
  });
@@ -6153,7 +6472,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6153
6472
  }
6154
6473
  case "POST /api/actions/disable": {
6155
6474
  const payload = await readJsonBody(request);
6156
- writeJson3(response, 200, {
6475
+ writeJson2(response, 200, {
6157
6476
  ok: true,
6158
6477
  result: await dependencies.disableManagedBundle(payload)
6159
6478
  });
@@ -6161,7 +6480,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6161
6480
  }
6162
6481
  case "POST /api/actions/enable": {
6163
6482
  const payload = await readJsonBody(request);
6164
- writeJson3(response, 200, {
6483
+ writeJson2(response, 200, {
6165
6484
  ok: true,
6166
6485
  result: await dependencies.enableManagedBundle(payload)
6167
6486
  });
@@ -6169,7 +6488,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6169
6488
  }
6170
6489
  case "POST /api/actions/remove": {
6171
6490
  const payload = await readJsonBody(request);
6172
- writeJson3(response, 200, {
6491
+ writeJson2(response, 200, {
6173
6492
  ok: true,
6174
6493
  result: await dependencies.removeManagedBundle(payload)
6175
6494
  });
@@ -6191,11 +6510,11 @@ async function handleManageRequest(request, response, assetRoot, dependencies) {
6191
6510
  return;
6192
6511
  }
6193
6512
  }
6194
- writeJson3(response, 404, { error: "Not found" });
6513
+ writeJson2(response, 404, { error: "Not found" });
6195
6514
  } catch (error) {
6196
6515
  const message = error instanceof Error ? error.message : String(error);
6197
6516
  const code = getErrorCode(error);
6198
- writeJson3(response, getErrorStatus(error), {
6517
+ writeJson2(response, getErrorStatus(error), {
6199
6518
  ...code ? { code } : {},
6200
6519
  error: message
6201
6520
  });
@@ -6266,6 +6585,10 @@ async function startManageServer(options = {}) {
6266
6585
  deviceAuth.user_code
6267
6586
  );
6268
6587
  authRequests.set(requestId, {
6588
+ serverUrl: resolvePersistedCliServerUrl2(
6589
+ serverUrl,
6590
+ authConfig.convexSiteUrl
6591
+ ),
6269
6592
  convexSiteUrl: authConfig.convexSiteUrl,
6270
6593
  deviceCode: deviceAuth.device_code,
6271
6594
  expiresIn: deviceAuth.expires_in,
@@ -6296,7 +6619,10 @@ async function startManageServer(options = {}) {
6296
6619
  pendingRequest.expiresIn,
6297
6620
  MANAGE_AUTH_TIMEOUT_SECONDS
6298
6621
  );
6299
- await persistApiKeyAsync(session.sessionToken);
6622
+ await persistApiKeyAsync(
6623
+ session.sessionToken,
6624
+ pendingRequest.serverUrl
6625
+ );
6300
6626
  return {
6301
6627
  authenticated: true,
6302
6628
  expiresAt: session.expiresAt,
@@ -6312,7 +6638,7 @@ async function startManageServer(options = {}) {
6312
6638
  if (!token) {
6313
6639
  return { authenticated: false };
6314
6640
  }
6315
- if (!token.startsWith(SESSION_TOKEN_PREFIX2)) {
6641
+ if (!token.startsWith(SESSION_TOKEN_PREFIX4)) {
6316
6642
  return { authenticated: true };
6317
6643
  }
6318
6644
  const serverUrl = (options.server ?? config.serverUrl).replace(
@@ -6337,7 +6663,7 @@ async function startManageServer(options = {}) {
6337
6663
  const signOut = options.signOut ?? (async () => {
6338
6664
  const config = await loadMergedConfigAsync();
6339
6665
  const token = config.token;
6340
- if (token?.startsWith(SESSION_TOKEN_PREFIX2)) {
6666
+ if (token?.startsWith(SESSION_TOKEN_PREFIX4)) {
6341
6667
  try {
6342
6668
  const serverUrl = (options.server ?? config.serverUrl).replace(
6343
6669
  TRAILING_SLASHES3,
@@ -6369,7 +6695,7 @@ async function startManageServer(options = {}) {
6369
6695
  handleManageRequest(request, response, assetRoot, dependencies).catch(
6370
6696
  (error) => {
6371
6697
  const message = error instanceof Error ? error.message : String(error);
6372
- writeJson3(response, 500, { error: message });
6698
+ writeJson2(response, 500, { error: message });
6373
6699
  }
6374
6700
  );
6375
6701
  });
@@ -6413,7 +6739,7 @@ async function manageCommand(options) {
6413
6739
  const server = await startManageServer({
6414
6740
  apiKey: options.apiKey,
6415
6741
  port: parsePort(options.port),
6416
- projectRoot: process10.cwd(),
6742
+ projectRoot: process11.cwd(),
6417
6743
  server: options.server
6418
6744
  });
6419
6745
  if (options.open !== false) {
@@ -6423,17 +6749,17 @@ async function manageCommand(options) {
6423
6749
  log.info("Press Ctrl+C to stop.");
6424
6750
  await new Promise((resolveClose, rejectClose) => {
6425
6751
  const shutdown = () => {
6426
- process10.off("SIGINT", shutdown);
6427
- process10.off("SIGTERM", shutdown);
6752
+ process11.off("SIGINT", shutdown);
6753
+ process11.off("SIGTERM", shutdown);
6428
6754
  server.close().then(resolveClose).catch(rejectClose);
6429
6755
  };
6430
- process10.once("SIGINT", shutdown);
6431
- process10.once("SIGTERM", shutdown);
6756
+ process11.once("SIGINT", shutdown);
6757
+ process11.once("SIGTERM", shutdown);
6432
6758
  });
6433
6759
  } catch (error) {
6434
6760
  const message = error instanceof Error ? error.message : String(error);
6435
6761
  log.error(message);
6436
- process10.exit(1);
6762
+ process11.exit(1);
6437
6763
  }
6438
6764
  }
6439
6765
 
@@ -6441,7 +6767,7 @@ async function manageCommand(options) {
6441
6767
  init_esm_shims();
6442
6768
  import { rm as rm6 } from "fs/promises";
6443
6769
  import { join as join13, resolve as resolve7 } from "path";
6444
- import process11 from "process";
6770
+ import process12 from "process";
6445
6771
  init_tui();
6446
6772
  async function selectInstallScope(options) {
6447
6773
  if (options.global != null) {
@@ -6460,7 +6786,7 @@ async function selectInstallScope(options) {
6460
6786
  });
6461
6787
  if (isCancel(result)) {
6462
6788
  cancel("Install cancelled.");
6463
- process11.exit(0);
6789
+ process12.exit(0);
6464
6790
  }
6465
6791
  return result;
6466
6792
  }
@@ -6495,7 +6821,7 @@ async function selectInstallAgents(options, global) {
6495
6821
  });
6496
6822
  if (isCancel(selected)) {
6497
6823
  cancel("Install cancelled.");
6498
- process11.exit(0);
6824
+ process12.exit(0);
6499
6825
  }
6500
6826
  return selected;
6501
6827
  }
@@ -6512,7 +6838,7 @@ async function confirmHooksOptIn(hookCount, options) {
6512
6838
  });
6513
6839
  if (isCancel(result)) {
6514
6840
  cancel("Install cancelled.");
6515
- process11.exit(0);
6841
+ process12.exit(0);
6516
6842
  }
6517
6843
  return result;
6518
6844
  }
@@ -6537,7 +6863,7 @@ async function marketplaceLibraryCommand(options) {
6537
6863
  log.error(
6538
6864
  error instanceof Error ? error.message : "Failed to fetch library"
6539
6865
  );
6540
- process11.exit(1);
6866
+ process12.exit(1);
6541
6867
  }
6542
6868
  }
6543
6869
  async function resolveAndInstallPack(slug, options, overrides) {
@@ -6560,7 +6886,8 @@ async function resolveAndInstallPack(slug, options, overrides) {
6560
6886
  manifest,
6561
6887
  agents: agents2,
6562
6888
  global,
6563
- allowHooks: options.allowHooks
6889
+ allowHooks: options.allowHooks,
6890
+ serverUrl: options.server
6564
6891
  });
6565
6892
  }
6566
6893
  async function marketplaceInstallCommand(slug, options) {
@@ -6571,7 +6898,7 @@ async function marketplaceInstallCommand(slug, options) {
6571
6898
  log.error(
6572
6899
  error instanceof Error ? error.message : "Failed to install pack"
6573
6900
  );
6574
- process11.exit(1);
6901
+ process12.exit(1);
6575
6902
  }
6576
6903
  }
6577
6904
  function addSkillToPack(packMap, slug, versionId, skillName, agent, installPath) {
@@ -6625,7 +6952,7 @@ async function marketplaceUpdateCommand(slug, options) {
6625
6952
  const toUpdate = slug ? installed.filter((p) => p.slug === slug) : installed;
6626
6953
  if (toUpdate.length === 0) {
6627
6954
  log.error(`Pack "${slug}" is not installed.`);
6628
- process11.exit(1);
6955
+ process12.exit(1);
6629
6956
  }
6630
6957
  let updated = 0;
6631
6958
  for (const pack of toUpdate) {
@@ -6645,7 +6972,7 @@ async function marketplaceUpdateCommand(slug, options) {
6645
6972
  log.error(
6646
6973
  error instanceof Error ? error.message : "Failed to update packs"
6647
6974
  );
6648
- process11.exit(1);
6975
+ process12.exit(1);
6649
6976
  }
6650
6977
  }
6651
6978
  async function selectPacksToRemove(installed, slug, options) {
@@ -6671,7 +6998,7 @@ async function selectPacksToRemove(installed, slug, options) {
6671
6998
  });
6672
6999
  if (isCancel(selected)) {
6673
7000
  cancel("Remove cancelled.");
6674
- process11.exit(0);
7001
+ process12.exit(0);
6675
7002
  }
6676
7003
  return selected;
6677
7004
  }
@@ -6709,7 +7036,7 @@ async function marketplaceRemoveCommand(slug, options) {
6709
7036
  });
6710
7037
  if (isCancel(confirmed) || !confirmed) {
6711
7038
  cancel("Remove cancelled.");
6712
- process11.exit(0);
7039
+ process12.exit(0);
6713
7040
  }
6714
7041
  }
6715
7042
  for (const pack of toRemove) {
@@ -6720,7 +7047,7 @@ async function marketplaceRemoveCommand(slug, options) {
6720
7047
  log.error(
6721
7048
  error instanceof Error ? error.message : "Failed to remove packs"
6722
7049
  );
6723
- process11.exit(1);
7050
+ process12.exit(1);
6724
7051
  }
6725
7052
  }
6726
7053
 
@@ -6728,19 +7055,6 @@ async function marketplaceRemoveCommand(slug, options) {
6728
7055
  init_esm_shims();
6729
7056
  init_api();
6730
7057
  init_tui();
6731
- import process12 from "process";
6732
- var writeJson4 = (value) => {
6733
- process12.stdout.write(`${JSON.stringify(value, null, 2)}
6734
- `);
6735
- };
6736
- var exitWithError3 = (error) => {
6737
- const message = error instanceof Error ? error.message : String(error);
6738
- log.error(message);
6739
- process12.exit(1);
6740
- };
6741
- var fail2 = (message) => {
6742
- throw new Error(message);
6743
- };
6744
7058
  var parseContext = (contextJson) => {
6745
7059
  if (!contextJson) {
6746
7060
  return void 0;
@@ -6761,7 +7075,7 @@ var run2 = async (command, args, options) => {
6761
7075
  apiOptions
6762
7076
  );
6763
7077
  if (options.json) {
6764
- writeJson4(result);
7078
+ writeJson(result);
6765
7079
  return;
6766
7080
  }
6767
7081
  log.success(`profiles ${command} completed`);
@@ -6770,34 +7084,34 @@ async function profilesListCommand(options) {
6770
7084
  try {
6771
7085
  await run2("list", {}, options);
6772
7086
  } catch (error) {
6773
- exitWithError3(error);
7087
+ exitCommandError(error, options);
6774
7088
  }
6775
7089
  }
6776
7090
  async function profilesGetCommand(options) {
6777
7091
  try {
6778
7092
  if (!(options.id || options.name)) {
6779
- fail2("profiles get requires --id or --name");
7093
+ fail("profiles get requires --id or --name");
6780
7094
  }
6781
7095
  await run2("get", { id: options.id, name: options.name }, options);
6782
7096
  } catch (error) {
6783
- exitWithError3(error);
7097
+ exitCommandError(error, options);
6784
7098
  }
6785
7099
  }
6786
7100
  async function profilesCreateCommand(options) {
6787
7101
  try {
6788
- const name = options.name ?? fail2("profiles create requires --name");
7102
+ const name = options.name ?? fail("profiles create requires --name");
6789
7103
  await run2(
6790
7104
  "create",
6791
7105
  { name, context: parseContext(options.contextJson) },
6792
7106
  options
6793
7107
  );
6794
7108
  } catch (error) {
6795
- exitWithError3(error);
7109
+ exitCommandError(error, options);
6796
7110
  }
6797
7111
  }
6798
7112
  async function profilesUpdateCommand(options) {
6799
7113
  try {
6800
- const id = options.id ?? fail2("profiles update requires --id");
7114
+ const id = options.id ?? fail("profiles update requires --id");
6801
7115
  await run2(
6802
7116
  "update",
6803
7117
  {
@@ -6808,26 +7122,26 @@ async function profilesUpdateCommand(options) {
6808
7122
  options
6809
7123
  );
6810
7124
  } catch (error) {
6811
- exitWithError3(error);
7125
+ exitCommandError(error, options);
6812
7126
  }
6813
7127
  }
6814
7128
  async function profilesRemoveCommand(options) {
6815
7129
  try {
6816
- const id = options.id ?? fail2("profiles remove requires --id");
7130
+ const id = options.id ?? fail("profiles remove requires --id");
6817
7131
  if (!options.yes) {
6818
- fail2("profiles remove requires --yes");
7132
+ fail("profiles remove requires --yes");
6819
7133
  }
6820
7134
  await run2("remove", { id, yes: true }, options);
6821
7135
  } catch (error) {
6822
- exitWithError3(error);
7136
+ exitCommandError(error, options);
6823
7137
  }
6824
7138
  }
6825
7139
  async function profilesSetDefaultCommand(options) {
6826
7140
  try {
6827
- const id = options.id ?? fail2("profiles set-default requires --id");
7141
+ const id = options.id ?? fail("profiles set-default requires --id");
6828
7142
  await run2("set-default", { id }, options);
6829
7143
  } catch (error) {
6830
- exitWithError3(error);
7144
+ exitCommandError(error, options);
6831
7145
  }
6832
7146
  }
6833
7147
 
@@ -6835,14 +7149,6 @@ async function profilesSetDefaultCommand(options) {
6835
7149
  init_esm_shims();
6836
7150
  init_api();
6837
7151
  init_tui();
6838
- import process13 from "process";
6839
- var writeJson5 = (value) => {
6840
- process13.stdout.write(`${JSON.stringify(value, null, 2)}
6841
- `);
6842
- };
6843
- var fail3 = (message) => {
6844
- throw new Error(message);
6845
- };
6846
7152
  var run3 = async (command, args, options) => {
6847
7153
  const apiOptions = {
6848
7154
  ...options.server ? { serverUrl: options.server } : {},
@@ -6857,39 +7163,34 @@ var run3 = async (command, args, options) => {
6857
7163
  apiOptions
6858
7164
  );
6859
7165
  if (options.json) {
6860
- writeJson5(result);
7166
+ writeJson(result);
6861
7167
  return;
6862
7168
  }
6863
7169
  log.success(`projects ${command} completed`);
6864
7170
  };
6865
- var exitWithError4 = (error) => {
6866
- const message = error instanceof Error ? error.message : String(error);
6867
- log.error(message);
6868
- process13.exit(1);
6869
- };
6870
7171
  async function projectsGetCommand(options) {
6871
7172
  try {
6872
- const id = options.id ?? fail3("projects get requires --id");
7173
+ const id = options.id ?? fail("projects get requires --id");
6873
7174
  await run3("get", { id }, options);
6874
7175
  } catch (error) {
6875
- exitWithError4(error);
7176
+ exitCommandError(error, options);
6876
7177
  }
6877
7178
  }
6878
7179
  async function projectsCreateCommand(options) {
6879
7180
  try {
6880
- const name = options.name ?? fail3("projects create requires --name");
7181
+ const name = options.name ?? fail("projects create requires --name");
6881
7182
  await run3(
6882
7183
  "create",
6883
7184
  { name, description: options.description, orgId: options.orgId },
6884
7185
  options
6885
7186
  );
6886
7187
  } catch (error) {
6887
- exitWithError4(error);
7188
+ exitCommandError(error, options);
6888
7189
  }
6889
7190
  }
6890
7191
  async function projectsUpdateCommand(options) {
6891
7192
  try {
6892
- const id = options.id ?? fail3("projects update requires --id");
7193
+ const id = options.id ?? fail("projects update requires --id");
6893
7194
  await run3(
6894
7195
  "update",
6895
7196
  {
@@ -6900,18 +7201,18 @@ async function projectsUpdateCommand(options) {
6900
7201
  options
6901
7202
  );
6902
7203
  } catch (error) {
6903
- exitWithError4(error);
7204
+ exitCommandError(error, options);
6904
7205
  }
6905
7206
  }
6906
7207
  async function projectsRemoveCommand(options) {
6907
7208
  try {
6908
- const id = options.id ?? fail3("projects remove requires --id");
7209
+ const id = options.id ?? fail("projects remove requires --id");
6909
7210
  if (!options.yes) {
6910
- fail3("projects remove requires --yes");
7211
+ fail("projects remove requires --yes");
6911
7212
  }
6912
7213
  await run3("remove", { id, yes: true }, options);
6913
7214
  } catch (error) {
6914
- exitWithError4(error);
7215
+ exitCommandError(error, options);
6915
7216
  }
6916
7217
  }
6917
7218
 
@@ -6919,11 +7220,6 @@ async function projectsRemoveCommand(options) {
6919
7220
  init_esm_shims();
6920
7221
  init_api();
6921
7222
  init_tui();
6922
- import process14 from "process";
6923
- var writeJson6 = (value) => {
6924
- process14.stdout.write(`${JSON.stringify(value, null, 2)}
6925
- `);
6926
- };
6927
7223
  var parseCsv3 = (input) => {
6928
7224
  if (!input) {
6929
7225
  return void 0;
@@ -6931,14 +7227,6 @@ var parseCsv3 = (input) => {
6931
7227
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
6932
7228
  return values.length > 0 ? values : void 0;
6933
7229
  };
6934
- var exitWithError5 = (error) => {
6935
- const message = error instanceof Error ? error.message : String(error);
6936
- log.error(message);
6937
- process14.exit(1);
6938
- };
6939
- var fail4 = (message) => {
6940
- throw new Error(message);
6941
- };
6942
7230
  var run4 = async (command, args, options) => {
6943
7231
  const apiOptions = {
6944
7232
  ...options.server ? { serverUrl: options.server } : {},
@@ -6953,69 +7241,69 @@ var run4 = async (command, args, options) => {
6953
7241
  apiOptions
6954
7242
  );
6955
7243
  if (options.json) {
6956
- writeJson6(result);
7244
+ writeJson(result);
6957
7245
  return;
6958
7246
  }
6959
7247
  log.success(`references ${command} completed`);
6960
7248
  };
6961
7249
  async function referencesListCommand(options) {
6962
7250
  try {
6963
- const ruleId = options.ruleId ?? fail4("references list requires --rule-id");
7251
+ const ruleId = options.ruleId ?? fail("references list requires --rule-id");
6964
7252
  await run4("list", { ruleId }, options);
6965
7253
  } catch (error) {
6966
- exitWithError5(error);
7254
+ exitCommandError(error, options);
6967
7255
  }
6968
7256
  }
6969
7257
  async function referencesGetCommand(options) {
6970
7258
  try {
6971
- const id = options.id ?? fail4("references get requires --id");
7259
+ const id = options.id ?? fail("references get requires --id");
6972
7260
  await run4("get", { id }, options);
6973
7261
  } catch (error) {
6974
- exitWithError5(error);
7262
+ exitCommandError(error, options);
6975
7263
  }
6976
7264
  }
6977
7265
  async function referencesCreateCommand(options) {
6978
7266
  try {
6979
- const ruleId = options.ruleId ?? fail4("references create requires --rule-id");
6980
- const file = options.file ?? fail4("references create requires --file");
7267
+ const ruleId = options.ruleId ?? fail("references create requires --rule-id");
7268
+ const file = options.file ?? fail("references create requires --file");
6981
7269
  await run4("create", { ruleId, file }, options);
6982
7270
  } catch (error) {
6983
- exitWithError5(error);
7271
+ exitCommandError(error, options);
6984
7272
  }
6985
7273
  }
6986
7274
  async function referencesUpdateCommand(options) {
6987
7275
  try {
6988
- const id = options.id ?? fail4("references update requires --id");
7276
+ const id = options.id ?? fail("references update requires --id");
6989
7277
  await run4(
6990
7278
  "update",
6991
7279
  { id, label: options.label, replaceFile: options.replaceFile },
6992
7280
  options
6993
7281
  );
6994
7282
  } catch (error) {
6995
- exitWithError5(error);
7283
+ exitCommandError(error, options);
6996
7284
  }
6997
7285
  }
6998
7286
  async function referencesRemoveCommand(options) {
6999
7287
  try {
7000
- const id = options.id ?? fail4("references remove requires --id");
7288
+ const id = options.id ?? fail("references remove requires --id");
7001
7289
  if (!options.yes) {
7002
- fail4("references remove requires --yes");
7290
+ fail("references remove requires --yes");
7003
7291
  }
7004
7292
  await run4("remove", { id, yes: true }, options);
7005
7293
  } catch (error) {
7006
- exitWithError5(error);
7294
+ exitCommandError(error, options);
7007
7295
  }
7008
7296
  }
7009
7297
  async function referencesReorderCommand(options) {
7010
7298
  try {
7011
- const ruleId = options.ruleId ?? fail4("references reorder requires --rule-id");
7299
+ const ruleId = options.ruleId ?? fail("references reorder requires --rule-id");
7012
7300
  const orderedIds = parseCsv3(options.orderedIds);
7013
7301
  if (!orderedIds || orderedIds.length === 0) {
7014
- fail4("references reorder requires --ordered-ids");
7302
+ fail("references reorder requires --ordered-ids");
7015
7303
  }
7016
7304
  await run4("reorder", { ruleId, orderedIds }, options);
7017
7305
  } catch (error) {
7018
- exitWithError5(error);
7306
+ exitCommandError(error, options);
7019
7307
  }
7020
7308
  }
7021
7309
 
@@ -7023,7 +7311,7 @@ async function referencesReorderCommand(options) {
7023
7311
  init_esm_shims();
7024
7312
  import { rm as rm7 } from "fs/promises";
7025
7313
  import { join as join14, resolve as resolve8 } from "path";
7026
- import process15 from "process";
7314
+ import process13 from "process";
7027
7315
  init_tui();
7028
7316
  async function collectInstalledSkills(detectedAgents, options) {
7029
7317
  const skillsToRemove = [];
@@ -7056,7 +7344,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7056
7344
  if (selected.length === 0) {
7057
7345
  log.error(`Skill '${options.skill}' not found.`);
7058
7346
  log.info("Run 'braid list' to see installed skills.");
7059
- process15.exit(1);
7347
+ process13.exit(1);
7060
7348
  }
7061
7349
  return selected;
7062
7350
  }
@@ -7075,7 +7363,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7075
7363
  });
7076
7364
  if (isCancel(result)) {
7077
7365
  cancel("Remove cancelled.");
7078
- process15.exit(0);
7366
+ process13.exit(0);
7079
7367
  }
7080
7368
  return result;
7081
7369
  }
@@ -7089,7 +7377,7 @@ async function confirmRemoval(selectedCount, options) {
7089
7377
  });
7090
7378
  if (isCancel(confirmed) || !confirmed) {
7091
7379
  cancel("Remove cancelled.");
7092
- process15.exit(0);
7380
+ process13.exit(0);
7093
7381
  }
7094
7382
  }
7095
7383
  async function removeSkill(skill, removeSpinner) {
@@ -7153,7 +7441,7 @@ async function removeCommand(options) {
7153
7441
  removeSpinner.stop("Remove failed");
7154
7442
  const message = error instanceof Error ? error.message : String(error);
7155
7443
  log.error(message);
7156
- process15.exit(1);
7444
+ process13.exit(1);
7157
7445
  }
7158
7446
  }
7159
7447
 
@@ -7212,6 +7500,7 @@ async function retractCommand(source, _options) {
7212
7500
  // src/commands/rollback.ts
7213
7501
  init_esm_shims();
7214
7502
  init_api();
7503
+ init_config();
7215
7504
  init_lockfile();
7216
7505
  init_tui();
7217
7506
  var PUBLIC_SOURCE_REGEX2 = /^@([a-z0-9-]+)\/([a-z0-9-]+)$/;
@@ -7244,6 +7533,8 @@ async function findPreviousActiveVersion(args) {
7244
7533
  return null;
7245
7534
  }
7246
7535
  async function rollbackCommand(source, options) {
7536
+ const config = await loadMergedConfigAsync();
7537
+ const serverUrl = options.server ?? config.serverUrl;
7247
7538
  intro(`Rolling back ${source}`);
7248
7539
  const rollbackSpinner = spinner();
7249
7540
  rollbackSpinner.start("Reading lockfile...");
@@ -7308,7 +7599,7 @@ async function rollbackCommand(source, options) {
7308
7599
  slug: publicSource.slug,
7309
7600
  currentVersion: parsed.version,
7310
7601
  ruleIds: currentValue.ruleIds,
7311
- server: options.server
7602
+ server: serverUrl
7312
7603
  });
7313
7604
  if (!result) {
7314
7605
  rollbackSpinner.stop("No active version found");
@@ -7351,7 +7642,6 @@ async function rollbackCommand(source, options) {
7351
7642
  init_esm_shims();
7352
7643
  init_api();
7353
7644
  init_tui();
7354
- import process16 from "process";
7355
7645
  var parseCsv4 = (input) => {
7356
7646
  if (!input) {
7357
7647
  return void 0;
@@ -7359,18 +7649,6 @@ var parseCsv4 = (input) => {
7359
7649
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
7360
7650
  return values.length > 0 ? values : void 0;
7361
7651
  };
7362
- var writeJson7 = (value) => {
7363
- process16.stdout.write(`${JSON.stringify(value, null, 2)}
7364
- `);
7365
- };
7366
- var exitWithError6 = (error) => {
7367
- const message = error instanceof Error ? error.message : String(error);
7368
- log.error(message);
7369
- process16.exit(1);
7370
- };
7371
- var fail5 = (message) => {
7372
- throw new Error(message);
7373
- };
7374
7652
  var run5 = async (command, args, options) => {
7375
7653
  const apiOptions = {
7376
7654
  ...options.server ? { serverUrl: options.server } : {},
@@ -7385,23 +7663,23 @@ var run5 = async (command, args, options) => {
7385
7663
  apiOptions
7386
7664
  );
7387
7665
  if (options.json) {
7388
- writeJson7(result);
7666
+ writeJson(result);
7389
7667
  return;
7390
7668
  }
7391
7669
  log.success(`rules ${command} completed`);
7392
7670
  };
7393
7671
  async function rulesGetCommand(options) {
7394
7672
  try {
7395
- const id = options.id ?? fail5("rules get requires --id");
7673
+ const id = options.id ?? fail("rules get requires --id");
7396
7674
  await run5("get", { id }, options);
7397
7675
  } catch (error) {
7398
- exitWithError6(error);
7676
+ exitCommandError(error, options);
7399
7677
  }
7400
7678
  }
7401
7679
  async function rulesCreateCommand(options) {
7402
7680
  try {
7403
- const title = options.title ?? fail5("rules create requires --title");
7404
- const content = options.content ?? fail5("rules create requires --content");
7681
+ const title = options.title ?? fail("rules create requires --title");
7682
+ const content = options.content ?? fail("rules create requires --content");
7405
7683
  await run5(
7406
7684
  "create",
7407
7685
  {
@@ -7414,12 +7692,12 @@ async function rulesCreateCommand(options) {
7414
7692
  options
7415
7693
  );
7416
7694
  } catch (error) {
7417
- exitWithError6(error);
7695
+ exitCommandError(error, options);
7418
7696
  }
7419
7697
  }
7420
7698
  async function rulesUpdateCommand(options) {
7421
7699
  try {
7422
- const id = options.id ?? fail5("rules update requires --id");
7700
+ const id = options.id ?? fail("rules update requires --id");
7423
7701
  await run5(
7424
7702
  "update",
7425
7703
  {
@@ -7432,120 +7710,120 @@ async function rulesUpdateCommand(options) {
7432
7710
  options
7433
7711
  );
7434
7712
  } catch (error) {
7435
- exitWithError6(error);
7713
+ exitCommandError(error, options);
7436
7714
  }
7437
7715
  }
7438
7716
  async function rulesRemoveCommand(options) {
7439
7717
  try {
7440
- const id = options.id ?? fail5("rules remove requires --id");
7718
+ const id = options.id ?? fail("rules remove requires --id");
7441
7719
  if (!options.yes) {
7442
- fail5("rules remove requires --yes");
7720
+ fail("rules remove requires --yes");
7443
7721
  }
7444
7722
  await run5("remove", { id, yes: true }, options);
7445
7723
  } catch (error) {
7446
- exitWithError6(error);
7724
+ exitCommandError(error, options);
7447
7725
  }
7448
7726
  }
7449
7727
  async function rulesEnableCommand(options) {
7450
7728
  try {
7451
- const id = options.id ?? fail5("rules enable requires --id");
7729
+ const id = options.id ?? fail("rules enable requires --id");
7452
7730
  await run5("enable", { id }, options);
7453
7731
  } catch (error) {
7454
- exitWithError6(error);
7732
+ exitCommandError(error, options);
7455
7733
  }
7456
7734
  }
7457
7735
  async function rulesDisableCommand(options) {
7458
7736
  try {
7459
- const id = options.id ?? fail5("rules disable requires --id");
7737
+ const id = options.id ?? fail("rules disable requires --id");
7460
7738
  await run5("disable", { id }, options);
7461
7739
  } catch (error) {
7462
- exitWithError6(error);
7740
+ exitCommandError(error, options);
7463
7741
  }
7464
7742
  }
7465
7743
  async function rulesMoveCommand(options) {
7466
7744
  try {
7467
- const id = options.id ?? fail5("rules move requires --id");
7468
- const projectId = options.projectId ?? fail5("rules move requires --project-id");
7745
+ const id = options.id ?? fail("rules move requires --id");
7746
+ const projectId = options.projectId ?? fail("rules move requires --project-id");
7469
7747
  await run5("move", { id, projectId }, options);
7470
7748
  } catch (error) {
7471
- exitWithError6(error);
7749
+ exitCommandError(error, options);
7472
7750
  }
7473
7751
  }
7474
7752
  async function rulesDuplicateCommand(options) {
7475
7753
  try {
7476
- const id = options.id ?? fail5("rules duplicate requires --id");
7754
+ const id = options.id ?? fail("rules duplicate requires --id");
7477
7755
  await run5(
7478
7756
  "duplicate",
7479
7757
  { id, targetProjectId: options.targetProjectId },
7480
7758
  options
7481
7759
  );
7482
7760
  } catch (error) {
7483
- exitWithError6(error);
7761
+ exitCommandError(error, options);
7484
7762
  }
7485
7763
  }
7486
7764
  async function rulesForkCommand(options) {
7487
7765
  try {
7488
- const id = options.id ?? fail5("rules fork requires --id");
7766
+ const id = options.id ?? fail("rules fork requires --id");
7489
7767
  await run5(
7490
7768
  "fork",
7491
7769
  { id, targetProjectId: options.targetProjectId },
7492
7770
  options
7493
7771
  );
7494
7772
  } catch (error) {
7495
- exitWithError6(error);
7773
+ exitCommandError(error, options);
7496
7774
  }
7497
7775
  }
7498
7776
  async function rulesSyncStatusCommand(options) {
7499
7777
  try {
7500
7778
  await run5("sync-status", { id: options.id }, options);
7501
7779
  } catch (error) {
7502
- exitWithError6(error);
7780
+ exitCommandError(error, options);
7503
7781
  }
7504
7782
  }
7505
7783
  async function rulesSyncHistoryCommand(options) {
7506
7784
  try {
7507
- const id = options.id ?? fail5("rules sync-history requires --id");
7785
+ const id = options.id ?? fail("rules sync-history requires --id");
7508
7786
  await run5("sync-history", { id }, options);
7509
7787
  } catch (error) {
7510
- exitWithError6(error);
7788
+ exitCommandError(error, options);
7511
7789
  }
7512
7790
  }
7513
7791
  async function rulesSyncEnableCommand(options) {
7514
7792
  try {
7515
- const id = options.id ?? fail5("rules sync-enable requires --id");
7793
+ const id = options.id ?? fail("rules sync-enable requires --id");
7516
7794
  await run5("sync-enable", { id }, options);
7517
7795
  } catch (error) {
7518
- exitWithError6(error);
7796
+ exitCommandError(error, options);
7519
7797
  }
7520
7798
  }
7521
7799
  async function rulesSyncDisableCommand(options) {
7522
7800
  try {
7523
- const id = options.id ?? fail5("rules sync-disable requires --id");
7801
+ const id = options.id ?? fail("rules sync-disable requires --id");
7524
7802
  await run5("sync-disable", { id }, options);
7525
7803
  } catch (error) {
7526
- exitWithError6(error);
7804
+ exitCommandError(error, options);
7527
7805
  }
7528
7806
  }
7529
7807
  async function rulesSyncCheckCommand(options) {
7530
7808
  try {
7531
- const id = options.id ?? fail5("rules sync-check requires --id");
7809
+ const id = options.id ?? fail("rules sync-check requires --id");
7532
7810
  await run5("sync-check", { id }, options);
7533
7811
  } catch (error) {
7534
- exitWithError6(error);
7812
+ exitCommandError(error, options);
7535
7813
  }
7536
7814
  }
7537
7815
  async function rulesSyncNowCommand(options) {
7538
7816
  try {
7539
- const id = options.id ?? fail5("rules sync-now requires --id");
7817
+ const id = options.id ?? fail("rules sync-now requires --id");
7540
7818
  await run5("sync-now", { id }, options);
7541
7819
  } catch (error) {
7542
- exitWithError6(error);
7820
+ exitCommandError(error, options);
7543
7821
  }
7544
7822
  }
7545
7823
 
7546
7824
  // src/commands/scaffold.ts
7547
7825
  init_esm_shims();
7548
- import process17 from "process";
7826
+ import process14 from "process";
7549
7827
 
7550
7828
  // src/lib/scaffold.ts
7551
7829
  init_esm_shims();
@@ -8505,11 +8783,11 @@ var normalizeReferenceList = (referenceLabel, availableNamesLabel, values, avail
8505
8783
  };
8506
8784
  var exitCancelled2 = () => {
8507
8785
  cancel("Scaffold cancelled.");
8508
- process17.exit(0);
8786
+ process14.exit(0);
8509
8787
  };
8510
- var exitWithError7 = (message) => {
8788
+ var exitWithError = (message) => {
8511
8789
  log.error(message);
8512
- process17.exit(1);
8790
+ process14.exit(1);
8513
8791
  };
8514
8792
  var requirePromptValue = (value) => {
8515
8793
  if (isCancel(value)) {
@@ -8522,7 +8800,7 @@ var resolveType = async (options) => {
8522
8800
  return options.type;
8523
8801
  }
8524
8802
  if (options.yes) {
8525
- return exitWithError7("--type is required when using --yes");
8803
+ return exitWithError("--type is required when using --yes");
8526
8804
  }
8527
8805
  const selected = requirePromptValue(
8528
8806
  await select({
@@ -8552,7 +8830,7 @@ var resolveName = async (options) => {
8552
8830
  return options.name.trim();
8553
8831
  }
8554
8832
  if (options.yes) {
8555
- return exitWithError7("--name is required when using --yes");
8833
+ return exitWithError("--name is required when using --yes");
8556
8834
  }
8557
8835
  const value = requirePromptValue(
8558
8836
  await text({
@@ -8638,7 +8916,7 @@ var resolveOptionalReference = async (label, flagValue, options, availableNames)
8638
8916
  return selected || void 0;
8639
8917
  };
8640
8918
  var buildScaffoldInput = async (options) => {
8641
- const context = await inspectScaffoldDirectory(process17.cwd());
8919
+ const context = await inspectScaffoldDirectory(process14.cwd());
8642
8920
  const availableSkillNames = context.manifest.skills.map(
8643
8921
  (entry) => entry.name
8644
8922
  );
@@ -8692,7 +8970,7 @@ var buildScaffoldInput = async (options) => {
8692
8970
  availableWorkflowNames
8693
8971
  ) : void 0;
8694
8972
  return {
8695
- cwd: process17.cwd(),
8973
+ cwd: process14.cwd(),
8696
8974
  type,
8697
8975
  name,
8698
8976
  title,
@@ -8721,10 +8999,10 @@ var finishScaffoldSuccess = (type, name, createdFiles) => {
8721
8999
  var isMockedProcessExitError = (error) => error instanceof Error && error.message.startsWith("process.exit:");
8722
9000
  var handleScaffoldCommandFailure = (error) => {
8723
9001
  if (error instanceof ScaffoldError) {
8724
- exitWithError7(error.message);
9002
+ exitWithError(error.message);
8725
9003
  }
8726
9004
  if (error instanceof Error && !isMockedProcessExitError(error)) {
8727
- exitWithError7(error.message);
9005
+ exitWithError(error.message);
8728
9006
  }
8729
9007
  throw error;
8730
9008
  };
@@ -9174,10 +9452,16 @@ async function updateCommand(options) {
9174
9452
  }
9175
9453
 
9176
9454
  // src/index.ts
9455
+ init_tui();
9177
9456
  var require2 = createRequire(import.meta.url);
9178
9457
  var { version: PACKAGE_VERSION } = require2("../package.json");
9458
+ var COMMANDER_ERROR_PREFIX = /^error:\s*/i;
9179
9459
  var program = new Command();
9180
- program.name("braid").description("Install and manage braid prompt artifacts locally").version(PACKAGE_VERSION);
9460
+ program.name("braid").description("Install and manage braid prompt artifacts locally").version(PACKAGE_VERSION).configureOutput({
9461
+ outputError: (str) => {
9462
+ log.error(str.replace(COMMANDER_ERROR_PREFIX, "").trim());
9463
+ }
9464
+ });
9181
9465
  var auth = program.command("auth").description("Configure API key for braid authentication");
9182
9466
  auth.command("login", { isDefault: true }).description("Authenticate with braid via browser login or API key").option("-s, --server <url>", "braid server URL (for review apps, local dev)").option(
9183
9467
  "--token <token>",