@braid-cloud/cli 0.1.18 → 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(),
@@ -498,89 +523,226 @@ var init_config = __esm({
498
523
  }
499
524
  });
500
525
 
501
- // src/lib/api.ts
502
- import { Data as Data3, Effect as Effect4, pipe as pipe4 } from "effect";
503
- 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;
504
- var init_api = __esm({
505
- "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"() {
506
703
  "use strict";
507
704
  init_esm_shims();
508
- init_config();
509
- TRAILING_SLASH_REGEX = /\/$/;
510
- ALLOW_UNTRUSTED_SERVER_ENV = "BRAID_ALLOW_UNTRUSTED_SERVER_URL";
511
- isTruthy = (value) => value === "1" || value === "true" || value === "yes";
512
- isLocalHost = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
513
- isTrustedApiServerUrl = (serverUrl) => {
514
- try {
515
- const parsed = new URL(serverUrl);
516
- const hostname2 = parsed.hostname;
517
- const isBraidHost = hostname2 === "braid.cloud" || hostname2.endsWith(".braid.cloud");
518
- const isConvexSite = hostname2.endsWith(".convex.site");
519
- if (parsed.protocol === "https:" && (isBraidHost || isConvexSite)) {
520
- return true;
521
- }
522
- if (parsed.protocol === "http:" && isLocalHost(hostname2)) {
523
- return true;
524
- }
525
- return false;
526
- } catch {
527
- 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";
528
729
  }
529
730
  };
530
- ApiError = class extends Data3.TaggedError("ApiError") {
531
- };
532
- AuthenticationError = class extends Data3.TaggedError("AuthenticationError") {
533
- };
534
- NetworkError = class extends Data3.TaggedError("NetworkError") {
535
- };
536
- resolveApiKey = (optionsApiKey) => {
537
- if (optionsApiKey) {
538
- return Effect4.succeed(optionsApiKey);
731
+ DeviceAuthDeniedError = class extends Error {
732
+ constructor() {
733
+ super("Device authorization was denied");
734
+ this.name = "DeviceAuthDeniedError";
539
735
  }
540
- return pipe4(
541
- Effect4.tryPromise({
542
- try: () => getApiKeyAsync(),
543
- catch: () => new NetworkError({ message: "Failed to read config" })
544
- }),
545
- Effect4.flatMap(
546
- (key) => key ? Effect4.succeed(key) : Effect4.fail(
547
- new AuthenticationError({
548
- message: 'No API key configured. Run "braid auth" to authenticate.'
549
- })
550
- )
551
- )
552
- );
553
736
  };
554
- resolveServerUrl = (optionsServerUrl) => {
555
- if (optionsServerUrl) {
556
- if (!(isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(optionsServerUrl))) {
557
- return Effect4.fail(
558
- new NetworkError({
559
- message: `Untrusted server URL '${optionsServerUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
560
- })
561
- );
562
- }
563
- return Effect4.succeed(optionsServerUrl);
737
+ DeviceAuthExpiredError = class extends Error {
738
+ constructor() {
739
+ super("Device authorization code has expired");
740
+ this.name = "DeviceAuthExpiredError";
564
741
  }
565
- return pipe4(
566
- Effect4.tryPromise({
567
- try: () => getServerUrlAsync(),
568
- catch: () => new NetworkError({ message: "Failed to read config" })
569
- }),
570
- Effect4.flatMap((serverUrl) => {
571
- if (isTruthy(process.env[ALLOW_UNTRUSTED_SERVER_ENV]) || isTrustedApiServerUrl(serverUrl)) {
572
- return Effect4.succeed(serverUrl);
573
- }
574
- return Effect4.fail(
575
- new NetworkError({
576
- message: `Untrusted server URL '${serverUrl}'. Set ${ALLOW_UNTRUSTED_SERVER_ENV}=true to override.`
577
- })
578
- );
579
- })
580
- );
581
742
  };
582
- normalizeApiServerBaseUrl = (serverUrl) => {
583
- 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, "");
584
746
  try {
585
747
  const url = new URL(trimmed);
586
748
  return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
@@ -588,13 +750,295 @@ var init_api = __esm({
588
750
  return trimmed;
589
751
  }
590
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
+ };
591
1035
  parseResponse = (response, json) => {
592
1036
  if (!response.ok) {
593
- const errorResponse = json;
594
- if (response.status === 401) {
1037
+ const errorResponse = getApiErrorResponse(json);
1038
+ if (isAuthenticationErrorResponse(response, json)) {
595
1039
  return Effect4.fail(
596
1040
  new AuthenticationError({
597
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1041
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
598
1042
  })
599
1043
  );
600
1044
  }
@@ -653,10 +1097,10 @@ var init_api = __esm({
653
1097
  Effect4.tryPromise({
654
1098
  try: () => fetch(url.toString(), {
655
1099
  method: "GET",
656
- headers: {
1100
+ headers: mergeAutomationHeaders({
657
1101
  "Content-Type": "application/json",
658
1102
  ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
659
- }
1103
+ })
660
1104
  }),
661
1105
  catch: (e) => new NetworkError({
662
1106
  message: `Failed to connect to ${serverUrl}`,
@@ -666,7 +1110,7 @@ var init_api = __esm({
666
1110
  Effect4.flatMap(
667
1111
  (response) => pipe4(
668
1112
  Effect4.tryPromise({
669
- try: () => response.json(),
1113
+ try: () => readJsonResponseAsync(response),
670
1114
  catch: () => new NetworkError({ message: "Failed to parse API response" })
671
1115
  }),
672
1116
  Effect4.flatMap((json) => parseResponse(response, json))
@@ -675,11 +1119,11 @@ var init_api = __esm({
675
1119
  );
676
1120
  parseScopeOptionsResponse = (response, json) => {
677
1121
  if (!response.ok) {
678
- const errorResponse = json;
679
- if (response.status === 401) {
1122
+ const errorResponse = getApiErrorResponse(json);
1123
+ if (isAuthenticationErrorResponse(response, json)) {
680
1124
  return Effect4.fail(
681
1125
  new AuthenticationError({
682
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1126
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
683
1127
  })
684
1128
  );
685
1129
  }
@@ -698,33 +1142,37 @@ var init_api = __esm({
698
1142
  apiKey: resolveApiKey(options.apiKey),
699
1143
  serverUrl: resolveServerUrl(options.serverUrl)
700
1144
  }),
701
- Effect4.flatMap(({ apiKey, serverUrl }) => {
702
- const url = buildApiUrl(serverUrl, "/api/skills/scope-options");
703
- return pipe4(
704
- Effect4.tryPromise({
705
- try: () => fetch(url.toString(), {
706
- method: "GET",
707
- headers: {
708
- Authorization: `Bearer ${apiKey}`,
709
- "Content-Type": "application/json"
710
- }
711
- }),
712
- catch: (e) => new NetworkError({
713
- message: `Failed to connect to ${serverUrl}`,
714
- cause: e
715
- })
716
- }),
717
- Effect4.flatMap(
718
- (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(
719
1151
  Effect4.tryPromise({
720
- try: () => response.json(),
721
- 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
+ })
722
1163
  }),
723
- Effect4.flatMap((json) => parseScopeOptionsResponse(response, json))
724
- )
725
- )
726
- );
727
- })
1164
+ Effect4.flatMap(
1165
+ (response) => pipe4(
1166
+ readJsonResponse(response),
1167
+ Effect4.flatMap(
1168
+ (json) => parseScopeOptionsResponse(response, json)
1169
+ )
1170
+ )
1171
+ )
1172
+ );
1173
+ })
1174
+ )
1175
+ )
728
1176
  );
729
1177
  buildLibraryOptionsUrl = (serverUrl, options) => {
730
1178
  const url = buildApiUrl(serverUrl, "/api/skills/library-options");
@@ -758,41 +1206,57 @@ var init_api = __esm({
758
1206
  }),
759
1207
  Effect4.flatMap(
760
1208
  ({ apiKey, serverUrl }) => pipe4(
761
- Effect4.tryPromise({
762
- try: () => fetch(buildLibraryOptionsUrl(serverUrl, options).toString(), {
763
- method: "GET",
764
- headers: {
765
- Authorization: `Bearer ${apiKey}`,
766
- "Content-Type": "application/json"
767
- }
768
- }),
769
- catch: (e) => new NetworkError({
770
- message: `Failed to connect to ${serverUrl}`,
771
- cause: e
772
- })
773
- }),
1209
+ resolveApiRequestServerUrl(serverUrl, apiKey),
774
1210
  Effect4.flatMap(
775
- (response) => pipe4(
1211
+ (apiServerUrl) => pipe4(
776
1212
  Effect4.tryPromise({
777
- try: () => response.json(),
778
- 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
+ })
779
1227
  }),
780
- Effect4.flatMap((json) => {
781
- if (!response.ok) {
782
- return parseResponse(response, json).pipe(
783
- Effect4.flatMap(
784
- () => Effect4.fail(
785
- new ApiError({
786
- message: "API request failed",
787
- code: "UNKNOWN_ERROR",
788
- status: response.status
789
- })
790
- )
791
- )
792
- );
793
- }
794
- return Effect4.succeed(json);
795
- })
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
+ )
796
1260
  )
797
1261
  )
798
1262
  )
@@ -803,10 +1267,15 @@ var init_api = __esm({
803
1267
  apiKey: options.demo ? Effect4.succeed(options.apiKey) : resolveApiKey(options.apiKey),
804
1268
  serverUrl: resolveServerUrl(options.serverUrl)
805
1269
  }),
806
- Effect4.flatMap(({ apiKey, serverUrl }) => {
807
- const url = buildExportUrl(serverUrl, options);
808
- return executeApiRequest(url, apiKey, serverUrl);
809
- })
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
+ )
810
1279
  );
811
1280
  runLifecycleCommand = (request, options = {}) => pipe4(
812
1281
  Effect4.all({
@@ -814,45 +1283,56 @@ var init_api = __esm({
814
1283
  serverUrl: resolveServerUrl(options.serverUrl)
815
1284
  }),
816
1285
  Effect4.flatMap(
817
- ({ apiKey, serverUrl }) => Effect4.tryPromise({
818
- try: async () => {
819
- const response = await fetch(
820
- buildApiUrl(serverUrl, "/api/lifecycle").toString(),
821
- {
822
- method: "POST",
823
- headers: {
824
- "Content-Type": "application/json",
825
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
826
- },
827
- body: JSON.stringify({ ...request, demo: options.demo })
828
- }
829
- );
830
- const json = await response.json();
831
- if (!response.ok) {
832
- const errorResponse = json;
833
- if (response.status === 401) {
834
- throw new AuthenticationError({
835
- 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
836
1331
  });
837
1332
  }
838
- throw new ApiError({
839
- message: errorResponse.error || "API request failed",
840
- code: errorResponse.code || "UNKNOWN_ERROR",
841
- status: response.status
842
- });
843
- }
844
- return json;
845
- },
846
- catch: (error) => {
847
- if (error instanceof ApiError || error instanceof AuthenticationError || error instanceof NetworkError) {
848
- return error;
849
- }
850
- return new NetworkError({
851
- message: `Failed to connect to ${serverUrl}`,
852
- cause: error
853
- });
854
- }
855
- })
1333
+ })
1334
+ )
1335
+ )
856
1336
  )
857
1337
  );
858
1338
  DEFAULT_PUBLIC_SERVER_URL = "https://braid.cloud";
@@ -879,7 +1359,7 @@ var init_api = __esm({
879
1359
  })
880
1360
  );
881
1361
  }
882
- const errorResponse = json;
1362
+ const errorResponse = getApiErrorResponse(json);
883
1363
  return Effect4.fail(
884
1364
  new ApiError({
885
1365
  message: errorResponse.error || "API request failed",
@@ -899,7 +1379,10 @@ var init_api = __esm({
899
1379
  }
900
1380
  return pipe4(
901
1381
  Effect4.tryPromise({
902
- try: () => fetch(url.toString(), { method: "GET" }),
1382
+ try: () => fetch(url.toString(), {
1383
+ method: "GET",
1384
+ headers: mergeAutomationHeaders()
1385
+ }),
903
1386
  catch: (e) => new NetworkError({
904
1387
  message: `Failed to connect to ${baseUrl}`,
905
1388
  cause: e
@@ -907,10 +1390,7 @@ var init_api = __esm({
907
1390
  }),
908
1391
  Effect4.flatMap(
909
1392
  (response) => pipe4(
910
- Effect4.tryPromise({
911
- try: () => response.json(),
912
- catch: () => new NetworkError({ message: "Failed to parse API response" })
913
- }),
1393
+ readJsonResponse(response),
914
1394
  Effect4.flatMap(
915
1395
  (json) => handlePublicApiResponse(response, json)
916
1396
  )
@@ -932,7 +1412,10 @@ var init_api = __esm({
932
1412
  }
933
1413
  return pipe4(
934
1414
  Effect4.tryPromise({
935
- try: () => fetch(url.toString(), { method: "GET" }),
1415
+ try: () => fetch(url.toString(), {
1416
+ method: "GET",
1417
+ headers: mergeAutomationHeaders()
1418
+ }),
936
1419
  catch: (e) => new NetworkError({
937
1420
  message: `Failed to connect to ${baseUrl}`,
938
1421
  cause: e
@@ -940,10 +1423,7 @@ var init_api = __esm({
940
1423
  }),
941
1424
  Effect4.flatMap(
942
1425
  (response) => pipe4(
943
- Effect4.tryPromise({
944
- try: () => response.json(),
945
- catch: () => new NetworkError({ message: "Failed to parse API response" })
946
- }),
1426
+ readJsonResponse(response),
947
1427
  Effect4.flatMap(
948
1428
  (json) => handlePublicApiResponse(response, json)
949
1429
  )
@@ -975,10 +1455,10 @@ var init_api = __esm({
975
1455
  url.searchParams.set("profile", "default");
976
1456
  const response = await fetch(url.toString(), {
977
1457
  method: "GET",
978
- headers: {
1458
+ headers: mergeAutomationHeaders({
979
1459
  Authorization: `Bearer ${apiKey}`,
980
1460
  "Content-Type": "application/json"
981
- }
1461
+ })
982
1462
  });
983
1463
  return response.status !== 401;
984
1464
  },
@@ -998,7 +1478,7 @@ var init_api = __esm({
998
1478
  });
999
1479
 
1000
1480
  // src/lib/tui.ts
1001
- import process5 from "process";
1481
+ import process7 from "process";
1002
1482
  import {
1003
1483
  cancel as clackCancel,
1004
1484
  confirm as clackConfirm,
@@ -1023,19 +1503,19 @@ function spinner() {
1023
1503
  return {
1024
1504
  start: (message) => {
1025
1505
  if (message) {
1026
- process5.stdout.write(`${message}
1506
+ process7.stdout.write(`${message}
1027
1507
  `);
1028
1508
  }
1029
1509
  },
1030
1510
  stop: (message) => {
1031
1511
  if (message) {
1032
- process5.stdout.write(`${message}
1512
+ process7.stdout.write(`${message}
1033
1513
  `);
1034
1514
  }
1035
1515
  },
1036
1516
  message: (message) => {
1037
1517
  if (message) {
1038
- process5.stdout.write(`${message}
1518
+ process7.stdout.write(`${message}
1039
1519
  `);
1040
1520
  }
1041
1521
  }
@@ -1045,7 +1525,7 @@ function intro(message) {
1045
1525
  if (isTTY) {
1046
1526
  clackIntro(message);
1047
1527
  } else {
1048
- process5.stdout.write(`
1528
+ process7.stdout.write(`
1049
1529
  ${message}
1050
1530
  `);
1051
1531
  }
@@ -1054,7 +1534,7 @@ function outro(message) {
1054
1534
  if (isTTY) {
1055
1535
  clackOutro(message);
1056
1536
  } else {
1057
- process5.stdout.write(`${message}
1537
+ process7.stdout.write(`${message}
1058
1538
 
1059
1539
  `);
1060
1540
  }
@@ -1064,7 +1544,7 @@ var init_tui = __esm({
1064
1544
  "src/lib/tui.ts"() {
1065
1545
  "use strict";
1066
1546
  init_esm_shims();
1067
- isTTY = Boolean(process5.stdout.isTTY);
1547
+ isTTY = Boolean(process7.stdout.isTTY);
1068
1548
  cancel = clackCancel;
1069
1549
  confirm = clackConfirm;
1070
1550
  isCancel = clackIsCancel;
@@ -1174,10 +1654,10 @@ var scope_exports = {};
1174
1654
  __export(scope_exports, {
1175
1655
  scopeCommand: () => scopeCommand
1176
1656
  });
1177
- import process7 from "process";
1657
+ import process9 from "process";
1178
1658
  function exitCancelled(message) {
1179
1659
  cancel(message);
1180
- process7.exit(0);
1660
+ process9.exit(0);
1181
1661
  }
1182
1662
  async function selectTargetFile(options) {
1183
1663
  if (options.file) {
@@ -1529,7 +2009,7 @@ async function scopeCommand(options) {
1529
2009
  } catch (error) {
1530
2010
  loadSpinner.stop("Failed to load scope options");
1531
2011
  log.error(error instanceof Error ? error.message : String(error));
1532
- process7.exit(1);
2012
+ process9.exit(1);
1533
2013
  }
1534
2014
  }
1535
2015
  var init_scope = __esm({
@@ -1568,7 +2048,7 @@ __export(lockfile_exports, {
1568
2048
  });
1569
2049
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
1570
2050
  import { join as join6 } from "path";
1571
- 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";
1572
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;
1573
2053
  var init_lockfile = __esm({
1574
2054
  "src/lib/lockfile.ts"() {
@@ -1578,13 +2058,13 @@ var init_lockfile = __esm({
1578
2058
  LOCKFILE_VERSION = 1;
1579
2059
  MARKETPLACE_KEY_REGEX = /^@([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1580
2060
  ORG_KEY_REGEX = /^([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1581
- LockfileReadError = class extends Data4.TaggedError("LockfileReadError") {
2061
+ LockfileReadError = class extends Data5.TaggedError("LockfileReadError") {
1582
2062
  };
1583
- LockfileWriteError = class extends Data4.TaggedError("LockfileWriteError") {
2063
+ LockfileWriteError = class extends Data5.TaggedError("LockfileWriteError") {
1584
2064
  };
1585
- LockfileVersionError = class extends Data4.TaggedError("LockfileVersionError") {
2065
+ LockfileVersionError = class extends Data5.TaggedError("LockfileVersionError") {
1586
2066
  };
1587
- LockfileCorruptError = class extends Data4.TaggedError("LockfileCorruptError") {
2067
+ LockfileCorruptError = class extends Data5.TaggedError("LockfileCorruptError") {
1588
2068
  };
1589
2069
  getLockfilePath = (dir) => join6(dir, LOCKFILE_FILENAME);
1590
2070
  emptyLockfile = () => ({
@@ -1593,27 +2073,27 @@ var init_lockfile = __esm({
1593
2073
  });
1594
2074
  readLockfile = (dir) => {
1595
2075
  const lockfilePath = getLockfilePath(dir);
1596
- return pipe5(
1597
- Effect5.tryPromise({
2076
+ return pipe6(
2077
+ Effect6.tryPromise({
1598
2078
  try: () => readFile3(lockfilePath, "utf-8"),
1599
2079
  catch: () => new LockfileReadError({ path: lockfilePath, cause: "not found" })
1600
2080
  }),
1601
- Effect5.catchTag(
2081
+ Effect6.catchTag(
1602
2082
  "LockfileReadError",
1603
- () => Effect5.succeed(null)
2083
+ () => Effect6.succeed(null)
1604
2084
  ),
1605
- Effect5.flatMap((content) => {
2085
+ Effect6.flatMap((content) => {
1606
2086
  if (content === null) {
1607
- return Effect5.succeed(null);
2087
+ return Effect6.succeed(null);
1608
2088
  }
1609
- return pipe5(
1610
- Effect5.try({
2089
+ return pipe6(
2090
+ Effect6.try({
1611
2091
  try: () => JSON.parse(content),
1612
2092
  catch: (cause) => new LockfileCorruptError({ path: lockfilePath, cause })
1613
2093
  }),
1614
- Effect5.flatMap((parsed) => {
2094
+ Effect6.flatMap((parsed) => {
1615
2095
  if (parsed.lockfileVersion !== LOCKFILE_VERSION) {
1616
- return Effect5.fail(
2096
+ return Effect6.fail(
1617
2097
  new LockfileVersionError({
1618
2098
  path: lockfilePath,
1619
2099
  expected: LOCKFILE_VERSION,
@@ -1621,7 +2101,7 @@ var init_lockfile = __esm({
1621
2101
  })
1622
2102
  );
1623
2103
  }
1624
- return Effect5.succeed(parsed);
2104
+ return Effect6.succeed(parsed);
1625
2105
  })
1626
2106
  );
1627
2107
  })
@@ -1629,7 +2109,7 @@ var init_lockfile = __esm({
1629
2109
  };
1630
2110
  writeLockfile = (dir, lockfile) => {
1631
2111
  const lockfilePath = getLockfilePath(dir);
1632
- return Effect5.tryPromise({
2112
+ return Effect6.tryPromise({
1633
2113
  try: () => writeFile4(
1634
2114
  lockfilePath,
1635
2115
  `${JSON.stringify(lockfile, null, 2)}
@@ -1646,26 +2126,26 @@ var init_lockfile = __esm({
1646
2126
  }
1647
2127
  return entry;
1648
2128
  };
1649
- upsertLockfileEntry = (dir, key, entry) => pipe5(
2129
+ upsertLockfileEntry = (dir, key, entry) => pipe6(
1650
2130
  readLockfile(dir),
1651
- Effect5.map((existing) => existing ?? emptyLockfile()),
1652
- Effect5.map((lockfile) => ({
2131
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2132
+ Effect6.map((lockfile) => ({
1653
2133
  ...lockfile,
1654
2134
  resolved: {
1655
2135
  ...lockfile.resolved,
1656
2136
  [key]: entry
1657
2137
  }
1658
2138
  })),
1659
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2139
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1660
2140
  );
1661
- removeLockfileEntry = (dir, key) => pipe5(
2141
+ removeLockfileEntry = (dir, key) => pipe6(
1662
2142
  readLockfile(dir),
1663
- Effect5.map((existing) => existing ?? emptyLockfile()),
1664
- Effect5.map((lockfile) => {
2143
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2144
+ Effect6.map((lockfile) => {
1665
2145
  const { [key]: _removed, ...rest } = lockfile.resolved;
1666
2146
  return { ...lockfile, resolved: rest };
1667
2147
  }),
1668
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2148
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1669
2149
  );
1670
2150
  buildLockfileKey = (source, handle, slug, version) => {
1671
2151
  const prefix = source === "marketplace" ? `@${handle}` : handle;
@@ -1692,10 +2172,10 @@ var init_lockfile = __esm({
1692
2172
  }
1693
2173
  return null;
1694
2174
  };
1695
- readLockfileAsync = (dir) => Effect5.runPromise(readLockfile(dir));
1696
- writeLockfileAsync = (dir, lockfile) => Effect5.runPromise(writeLockfile(dir, lockfile));
1697
- upsertLockfileEntryAsync = (dir, key, entry) => Effect5.runPromise(upsertLockfileEntry(dir, key, entry));
1698
- 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));
1699
2179
  }
1700
2180
  });
1701
2181
 
@@ -2244,42 +2724,289 @@ var resolveInstallPath = (agent, options) => {
2244
2724
  if (!agent.projectPath) {
2245
2725
  return void 0;
2246
2726
  }
2247
- const cwd = options.projectRoot ?? process2.cwd();
2248
- return join(cwd, agent.projectPath);
2249
- };
2250
- var resolveAgentsInstallPath = (agent, options) => {
2251
- if (options.global) {
2252
- return agent.agentsGlobalPath;
2727
+ const cwd = options.projectRoot ?? process2.cwd();
2728
+ return join(cwd, agent.projectPath);
2729
+ };
2730
+ var resolveAgentsInstallPath = (agent, options) => {
2731
+ if (options.global) {
2732
+ return agent.agentsGlobalPath;
2733
+ }
2734
+ if (!agent.agentsProjectPath) {
2735
+ return void 0;
2736
+ }
2737
+ const cwd = options.projectRoot ?? process2.cwd();
2738
+ return join(cwd, agent.agentsProjectPath);
2739
+ };
2740
+ var resolveRulesInstallPath = (agent, options) => {
2741
+ if (options.global) {
2742
+ return agent.rulesGlobalPath;
2743
+ }
2744
+ if (!agent.rulesProjectPath) {
2745
+ return void 0;
2746
+ }
2747
+ const cwd = options.projectRoot ?? process2.cwd();
2748
+ return join(cwd, agent.rulesProjectPath);
2749
+ };
2750
+ var resolveHookConfigPath = (agent, options) => {
2751
+ if (options.global) {
2752
+ return agent.hookGlobalConfigPath;
2753
+ }
2754
+ if (!agent.hookProjectConfigPath) {
2755
+ return void 0;
2756
+ }
2757
+ const cwd = options.projectRoot ?? process2.cwd();
2758
+ return join(cwd, agent.hookProjectConfigPath);
2759
+ };
2760
+
2761
+ // src/commands/agents.ts
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
2935
+ init_tui();
2936
+ var CommandValidationError = class extends Error {
2937
+ code = "CLI_VALIDATION_ERROR";
2938
+ constructor(message) {
2939
+ super(message);
2940
+ this.name = "CommandValidationError";
2941
+ }
2942
+ };
2943
+ var writeJson = (value) => {
2944
+ process8.stdout.write(`${JSON.stringify(value, null, 2)}
2945
+ `);
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
+ };
2253
2968
  }
2254
- if (!agent.agentsProjectPath) {
2255
- return void 0;
2969
+ if (error instanceof MarketplaceApiError) {
2970
+ return {
2971
+ error: error.message,
2972
+ code: error.code,
2973
+ status: error.status
2974
+ };
2256
2975
  }
2257
- const cwd = options.projectRoot ?? process2.cwd();
2258
- return join(cwd, agent.agentsProjectPath);
2259
- };
2260
- var resolveRulesInstallPath = (agent, options) => {
2261
- if (options.global) {
2262
- return agent.rulesGlobalPath;
2976
+ if (error instanceof CommandValidationError || error instanceof SyntaxError) {
2977
+ return {
2978
+ error: error.message,
2979
+ code: "CLI_VALIDATION_ERROR",
2980
+ status: 400
2981
+ };
2263
2982
  }
2264
- if (!agent.rulesProjectPath) {
2265
- return void 0;
2983
+ if (error instanceof Error) {
2984
+ return {
2985
+ error: error.message,
2986
+ code: "CLI_ERROR",
2987
+ status: 500
2988
+ };
2266
2989
  }
2267
- const cwd = options.projectRoot ?? process2.cwd();
2268
- return join(cwd, agent.rulesProjectPath);
2990
+ return {
2991
+ error: String(error),
2992
+ code: "CLI_ERROR",
2993
+ status: 500
2994
+ };
2269
2995
  };
2270
- var resolveHookConfigPath = (agent, options) => {
2271
- if (options.global) {
2272
- return agent.hookGlobalConfigPath;
2273
- }
2274
- if (!agent.hookProjectConfigPath) {
2275
- return void 0;
2996
+ var fail = (message) => {
2997
+ throw new CommandValidationError(message);
2998
+ };
2999
+ var exitCommandError = (error, options) => {
3000
+ if (options?.json) {
3001
+ writeJson(toJsonErrorOutput(error));
3002
+ process8.exit(1);
2276
3003
  }
2277
- const cwd = options.projectRoot ?? process2.cwd();
2278
- return join(cwd, agent.hookProjectConfigPath);
3004
+ const message = error instanceof Error ? error.message : String(error);
3005
+ log.error(message);
3006
+ process8.exit(1);
2279
3007
  };
2280
3008
 
2281
3009
  // src/commands/agents.ts
2282
- init_api();
2283
3010
  init_tui();
2284
3011
  var parseCsv = (input) => {
2285
3012
  if (!input) {
@@ -2288,18 +3015,6 @@ var parseCsv = (input) => {
2288
3015
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2289
3016
  return values.length > 0 ? values : void 0;
2290
3017
  };
2291
- var writeJson = (value) => {
2292
- process.stdout.write(`${JSON.stringify(value, null, 2)}
2293
- `);
2294
- };
2295
- var fail = (message) => {
2296
- throw new Error(message);
2297
- };
2298
- var exitWithError = (error) => {
2299
- const message = error instanceof Error ? error.message : String(error);
2300
- log.error(message);
2301
- process.exit(1);
2302
- };
2303
3018
  var run = (command, args, options) => {
2304
3019
  const apiOptions = {
2305
3020
  ...options.server ? { serverUrl: options.server } : {},
@@ -2403,7 +3118,7 @@ async function agentsListCommand(options) {
2403
3118
  log.success("sub-agents list completed");
2404
3119
  writeJson(result);
2405
3120
  } catch (error) {
2406
- exitWithError(error);
3121
+ exitCommandError(error, { json: options.json });
2407
3122
  }
2408
3123
  }
2409
3124
  async function agentsGetCommand(options) {
@@ -2417,7 +3132,7 @@ async function agentsGetCommand(options) {
2417
3132
  log.success("sub-agents get completed");
2418
3133
  writeJson(result);
2419
3134
  } catch (error) {
2420
- exitWithError(error);
3135
+ exitCommandError(error, { json: options.json });
2421
3136
  }
2422
3137
  }
2423
3138
  async function agentsCreateCommand(options) {
@@ -2445,7 +3160,7 @@ async function agentsCreateCommand(options) {
2445
3160
  }
2446
3161
  log.success("sub-agents create completed");
2447
3162
  } catch (error) {
2448
- exitWithError(error);
3163
+ exitCommandError(error, { json: options.json });
2449
3164
  }
2450
3165
  }
2451
3166
  async function agentsUpdateCommand(options) {
@@ -2470,7 +3185,7 @@ async function agentsUpdateCommand(options) {
2470
3185
  }
2471
3186
  log.success("sub-agents update completed");
2472
3187
  } catch (error) {
2473
- exitWithError(error);
3188
+ exitCommandError(error, { json: options.json });
2474
3189
  }
2475
3190
  }
2476
3191
  async function agentsRemoveCommand(options) {
@@ -2486,7 +3201,7 @@ async function agentsRemoveCommand(options) {
2486
3201
  }
2487
3202
  log.success("sub-agents remove completed");
2488
3203
  } catch (error) {
2489
- exitWithError(error);
3204
+ exitCommandError(error, { json: options.json });
2490
3205
  }
2491
3206
  }
2492
3207
  async function agentsInstallCommand(options) {
@@ -2522,354 +3237,46 @@ async function agentsInstallCommand(options) {
2522
3237
  }
2523
3238
  if (options.json) {
2524
3239
  writeJson(summary);
2525
- return;
2526
- }
2527
- for (const item of summary) {
2528
- log.info(
2529
- `${item.platform}: ${item.written} file(s) written${item.installPath ? ` to ${item.installPath}` : ""}`
2530
- );
2531
- for (const warning of item.warnings) {
2532
- log.warn(` warning: ${warning}`);
2533
- }
2534
- for (const error of item.errors) {
2535
- log.error(` error (${error.agent}): ${error.error}`);
2536
- }
2537
- }
2538
- log.success("sub-agents install completed");
2539
- } catch (error) {
2540
- exitWithError(error);
2541
- }
2542
- }
2543
-
2544
- // src/commands/auth.ts
2545
- init_esm_shims();
2546
- init_api();
2547
- init_config();
2548
- import process8 from "process";
2549
-
2550
- // src/lib/device-auth.ts
2551
- init_esm_shims();
2552
- init_braid_workspace();
2553
- import { execFile } from "child_process";
2554
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2555
- import { hostname, platform } from "os";
2556
- import { join as join4 } from "path";
2557
- import process6 from "process";
2558
- var TRAILING_SLASHES2 = /\/+$/;
2559
- var LEGACY_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
2560
- "api.braid.cloud",
2561
- "braid.cloud",
2562
- "www.braid.cloud"
2563
- ]);
2564
- var BRAID_APP_HOST = "app.braid.cloud";
2565
- var DEFAULT_BRAID_AUTH_HOSTS = /* @__PURE__ */ new Set([
2566
- ...LEGACY_BRAID_AUTH_HOSTS,
2567
- BRAID_APP_HOST
2568
- ]);
2569
- var LOOPBACK_HOSTS2 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
2570
- var LOCAL_AUTH_ENV_FILES = [
2571
- ["apps", "web", ".env.local"],
2572
- ["apps", "convex", ".env.local"]
2573
- ];
2574
- var LOCAL_AUTH_ENV_KEYS = ["APP_URL", "SITE_URL"];
2575
- var NEWLINE_REGEX2 = /\r?\n/;
2576
- var DeviceAuthTimeoutError = class extends Error {
2577
- constructor() {
2578
- super("Device authorization timed out");
2579
- this.name = "DeviceAuthTimeoutError";
2580
- }
2581
- };
2582
- var DeviceAuthDeniedError = class extends Error {
2583
- constructor() {
2584
- super("Device authorization was denied");
2585
- this.name = "DeviceAuthDeniedError";
2586
- }
2587
- };
2588
- var DeviceAuthExpiredError = class extends Error {
2589
- constructor() {
2590
- super("Device authorization code has expired");
2591
- this.name = "DeviceAuthExpiredError";
2592
- }
2593
- };
2594
- var sleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
2595
- var normalizeBaseUrl2 = (rawUrl) => {
2596
- const trimmed = rawUrl.replace(TRAILING_SLASHES2, "");
2597
- try {
2598
- const url = new URL(trimmed);
2599
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2600
- } catch {
2601
- return trimmed;
2602
- }
2603
- };
2604
- var normalizeLoopbackBaseUrl = (rawUrl) => {
2605
- const normalized = normalizeBaseUrl2(rawUrl);
2606
- try {
2607
- const url = new URL(normalized);
2608
- if (isLoopbackHost(url.hostname)) {
2609
- url.hostname = "localhost";
2610
- }
2611
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2612
- } catch {
2613
- return normalized;
2614
- }
2615
- };
2616
- var isLoopbackHost = (hostname2) => LOOPBACK_HOSTS2.has(hostname2);
2617
- var isLoopbackUrl = (rawUrl) => {
2618
- try {
2619
- return isLoopbackHost(new URL(rawUrl).hostname);
2620
- } catch {
2621
- return false;
2622
- }
2623
- };
2624
- var stripQuotes2 = (value) => {
2625
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2626
- return value.slice(1, -1);
2627
- }
2628
- return value;
2629
- };
2630
- var parseDotenv2 = (content) => {
2631
- const result = {};
2632
- for (const rawLine of content.split(NEWLINE_REGEX2)) {
2633
- const line = rawLine.trim();
2634
- if (!line || line.startsWith("#")) {
2635
- continue;
2636
- }
2637
- const separatorIndex = line.indexOf("=");
2638
- if (separatorIndex <= 0) {
2639
- continue;
2640
- }
2641
- const key = line.slice(0, separatorIndex).trim();
2642
- const value = line.slice(separatorIndex + 1).trim();
2643
- result[key] = stripQuotes2(value);
2644
- }
2645
- return result;
2646
- };
2647
- var resolveLocalAuthWebBaseUrlFromEnv = (env) => {
2648
- for (const key of LOCAL_AUTH_ENV_KEYS) {
2649
- const value = env[key];
2650
- if (value && isLoopbackUrl(value)) {
2651
- return normalizeLoopbackBaseUrl(value);
2652
- }
2653
- }
2654
- return void 0;
2655
- };
2656
- var resolveLocalAuthWebBaseUrlFromFiles = () => {
2657
- const workspaceRoot = findBraidWorkspaceRoot();
2658
- if (!workspaceRoot) {
2659
- return void 0;
2660
- }
2661
- for (const envFile of LOCAL_AUTH_ENV_FILES) {
2662
- const envPath = join4(workspaceRoot, ...envFile);
2663
- if (!existsSync3(envPath)) {
2664
- continue;
2665
- }
2666
- const localAppUrl = resolveLocalAuthWebBaseUrlFromEnv(
2667
- parseDotenv2(readFileSync3(envPath, "utf8"))
2668
- );
2669
- if (localAppUrl) {
2670
- return localAppUrl;
2671
- }
2672
- }
2673
- return void 0;
2674
- };
2675
- var resolveLocalAuthWebBaseUrl = () => resolveLocalAuthWebBaseUrlFromEnv(process6.env) ?? resolveLocalAuthWebBaseUrlFromFiles();
2676
- var normalizeServerBaseUrl = (serverUrl) => {
2677
- const trimmed = normalizeBaseUrl2(serverUrl);
2678
- try {
2679
- const url = new URL(trimmed);
2680
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2681
- } catch {
2682
- return trimmed;
2683
- }
2684
- };
2685
- var resolveCanonicalAuthWebBaseUrl = (serverUrl) => {
2686
- const normalized = normalizeServerBaseUrl(serverUrl);
2687
- try {
2688
- const url = new URL(normalized);
2689
- if (LEGACY_BRAID_AUTH_HOSTS.has(url.hostname)) {
2690
- url.hostname = BRAID_APP_HOST;
2691
- url.pathname = "/";
2692
- }
2693
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2694
- } catch {
2695
- return normalized;
2696
- }
2697
- };
2698
- var isHostedBraidAuthServer = (serverUrl) => {
2699
- try {
2700
- return DEFAULT_BRAID_AUTH_HOSTS.has(
2701
- new URL(normalizeServerBaseUrl(serverUrl)).hostname
2702
- );
2703
- } catch {
2704
- return false;
2705
- }
2706
- };
2707
- function resolveAuthWebBaseUrl(serverUrl) {
2708
- const canonicalBaseUrl = resolveCanonicalAuthWebBaseUrl(serverUrl);
2709
- try {
2710
- const url = new URL(canonicalBaseUrl);
2711
- const localAuthBaseUrl = resolveLocalAuthWebBaseUrl();
2712
- if (localAuthBaseUrl && (DEFAULT_BRAID_AUTH_HOSTS.has(url.hostname) || isLoopbackHost(url.hostname))) {
2713
- return localAuthBaseUrl;
2714
- }
2715
- return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
2716
- } catch {
2717
- return canonicalBaseUrl;
2718
- }
2719
- }
2720
- function buildAuthVerificationUrlFromBaseUrl(authBaseUrl, userCode) {
2721
- return `${authBaseUrl.replace(TRAILING_SLASHES2, "")}/cli/authorize?user_code=${encodeURIComponent(userCode)}`;
2722
- }
2723
- async function fetchAuthConfigFromBaseUrl(authBaseUrl) {
2724
- const response = await fetch(`${authBaseUrl}/api/cli/auth-config`);
2725
- if (!response.ok) {
2726
- const body = await response.text();
2727
- throw new Error(
2728
- `Failed to fetch auth config (${response.status}): ${body}`
2729
- );
2730
- }
2731
- return {
2732
- ...await response.json(),
2733
- verificationBaseUrl: authBaseUrl
2734
- };
2735
- }
2736
- async function fetchAuthConfig(serverUrl) {
2737
- const authBaseUrl = resolveAuthWebBaseUrl(serverUrl).replace(
2738
- TRAILING_SLASHES2,
2739
- ""
2740
- );
2741
- const canonicalAuthBaseUrl = resolveCanonicalAuthWebBaseUrl(
2742
- serverUrl
2743
- ).replace(TRAILING_SLASHES2, "");
2744
- try {
2745
- return await fetchAuthConfigFromBaseUrl(authBaseUrl);
2746
- } catch (error) {
2747
- if (authBaseUrl !== canonicalAuthBaseUrl && isHostedBraidAuthServer(serverUrl)) {
2748
- return fetchAuthConfigFromBaseUrl(canonicalAuthBaseUrl);
2749
- }
2750
- throw error;
2751
- }
2752
- }
2753
- async function initiateDeviceAuth(convexSiteUrl, deviceInfo) {
2754
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/authorize`;
2755
- const response = await fetch(url, {
2756
- method: "POST",
2757
- headers: { "Content-Type": "application/json" },
2758
- body: JSON.stringify(deviceInfo)
2759
- });
2760
- if (!response.ok) {
2761
- const body = await response.text();
2762
- throw new Error(
2763
- `Failed to initiate device authorization (${response.status}): ${body}`
2764
- );
2765
- }
2766
- return response.json();
2767
- }
2768
- async function pollForSession(convexSiteUrl, deviceCode, interval, expiresIn, timeoutSeconds) {
2769
- const tokenUrl = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/device/token`;
2770
- const deadline = Date.now() + Math.min(expiresIn, timeoutSeconds) * 1e3;
2771
- let currentInterval = interval;
2772
- while (Date.now() < deadline) {
2773
- await sleep(currentInterval * 1e3);
2774
- const response = await fetch(tokenUrl, {
2775
- method: "POST",
2776
- headers: { "Content-Type": "application/json" },
2777
- body: JSON.stringify({ device_code: deviceCode })
2778
- });
2779
- if (response.ok) {
2780
- const result = await response.json();
2781
- return {
2782
- sessionToken: result.session_token,
2783
- expiresAt: result.expires_at,
2784
- user: result.user
2785
- };
2786
- }
2787
- let errorCode;
2788
- try {
2789
- const errorBody = await response.json();
2790
- errorCode = errorBody.error;
2791
- } catch {
2792
- throw new Error(`Device auth polling error: HTTP ${response.status}`);
2793
- }
2794
- if (errorCode === "authorization_pending") {
2795
- continue;
2796
- }
2797
- if (errorCode === "slow_down") {
2798
- currentInterval += 5;
2799
- continue;
2800
- }
2801
- if (errorCode === "expired_token") {
2802
- throw new DeviceAuthExpiredError();
2803
- }
2804
- if (errorCode === "access_denied") {
2805
- throw new DeviceAuthDeniedError();
2806
- }
2807
- throw new Error(
2808
- `Device auth polling error: ${errorCode ?? `HTTP ${response.status}`}`
2809
- );
2810
- }
2811
- throw new DeviceAuthTimeoutError();
2812
- }
2813
- async function fetchSessionInfo(convexSiteUrl, sessionToken) {
2814
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
2815
- const response = await fetch(url, {
2816
- method: "GET",
2817
- headers: {
2818
- Authorization: `Bearer ${sessionToken}`
3240
+ return;
2819
3241
  }
2820
- });
2821
- if (response.status === 404) {
2822
- return null;
2823
- }
2824
- if (!response.ok) {
2825
- const body = await response.text();
2826
- throw new Error(
2827
- `Failed to fetch session info (${response.status}): ${body}`
2828
- );
2829
- }
2830
- return response.json();
2831
- }
2832
- async function revokeSession(convexSiteUrl, sessionToken) {
2833
- const url = `${convexSiteUrl.replace(TRAILING_SLASHES2, "")}/api/cli/sessions/me`;
2834
- const response = await fetch(url, {
2835
- method: "DELETE",
2836
- headers: {
2837
- Authorization: `Bearer ${sessionToken}`
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
+ }
2838
3252
  }
2839
- });
2840
- if (response.status === 404) {
2841
- return false;
2842
- }
2843
- if (!response.ok) {
2844
- const body = await response.text();
2845
- throw new Error(`Failed to revoke session (${response.status}): ${body}`);
2846
- }
2847
- return true;
2848
- }
2849
- var noop = () => {
2850
- };
2851
- function openBrowser(url) {
2852
- const currentPlatform = platform();
2853
- if (currentPlatform === "darwin") {
2854
- execFile("open", [url], noop);
2855
- } else if (currentPlatform === "win32") {
2856
- execFile("cmd", ["/c", "start", "", url], noop);
2857
- } else {
2858
- execFile("xdg-open", [url], noop);
3253
+ log.success("sub-agents install completed");
3254
+ } catch (error) {
3255
+ exitCommandError(error, { json: options.json });
2859
3256
  }
2860
3257
  }
2861
- function getDeviceInfo() {
2862
- return {
2863
- deviceName: hostname(),
2864
- deviceOs: platform(),
2865
- deviceHostname: hostname()
2866
- };
2867
- }
2868
3258
 
2869
3259
  // src/commands/auth.ts
3260
+ init_esm_shims();
3261
+ init_api();
3262
+ init_config();
3263
+ init_device_auth();
2870
3264
  init_tui();
2871
- 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"]);
2872
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
+ };
2873
3280
  async function configureDefaultScopeAsync(serverUrl) {
2874
3281
  const shouldConfigureScope = await confirm({
2875
3282
  message: "Configure organization and scope defaults now?",
@@ -2904,9 +3311,9 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2904
3311
  log.error(
2905
3312
  "The API key could not be validated. Please check your key and try again."
2906
3313
  );
2907
- process8.exit(1);
3314
+ process10.exit(1);
2908
3315
  }
2909
- await persistApiKeyAsync(apiKey);
3316
+ await persistApiKeyAsync(apiKey, serverUrl);
2910
3317
  stopAuthSpinner("API key validated and saved");
2911
3318
  if (options.scope !== false) {
2912
3319
  await configureDefaultScopeAsync(serverUrl);
@@ -2919,7 +3326,7 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2919
3326
  stopAuthSpinner("Validation failed");
2920
3327
  const message = error instanceof Error ? error.message : String(error);
2921
3328
  log.error(`Failed to validate API key: ${message}`);
2922
- process8.exit(1);
3329
+ process10.exit(1);
2923
3330
  }
2924
3331
  }
2925
3332
  function handlePollingError(error) {
@@ -2933,7 +3340,7 @@ function handlePollingError(error) {
2933
3340
  const message = error instanceof Error ? error.message : String(error);
2934
3341
  log.error(`Authentication failed: ${message}`);
2935
3342
  }
2936
- process8.exit(1);
3343
+ process10.exit(1);
2937
3344
  }
2938
3345
  async function deviceFlow(serverUrl, timeoutSeconds, options) {
2939
3346
  const configSpinner = spinner();
@@ -2946,7 +3353,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2946
3353
  configSpinner.stop("Failed to fetch auth configuration");
2947
3354
  const message = error instanceof Error ? error.message : String(error);
2948
3355
  log.error(`Could not connect to server: ${message}`);
2949
- process8.exit(1);
3356
+ process10.exit(1);
2950
3357
  }
2951
3358
  const deviceSpinner = spinner();
2952
3359
  deviceSpinner.start("Initiating device authorization...");
@@ -2961,7 +3368,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2961
3368
  deviceSpinner.stop("Failed to initiate device authorization");
2962
3369
  const message = error instanceof Error ? error.message : String(error);
2963
3370
  log.error(`Device authorization failed: ${message}`);
2964
- process8.exit(1);
3371
+ process10.exit(1);
2965
3372
  }
2966
3373
  const verificationUrl = buildAuthVerificationUrlFromBaseUrl(
2967
3374
  authConfig.verificationBaseUrl,
@@ -2995,9 +3402,13 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2995
3402
  pollSpinner.stop("Authentication failed");
2996
3403
  handlePollingError(error);
2997
3404
  }
2998
- await persistApiKeyAsync(session.sessionToken, authConfig.convexSiteUrl);
3405
+ const persistedServerUrl = resolvePersistedCliServerUrl(
3406
+ serverUrl,
3407
+ authConfig.convexSiteUrl
3408
+ );
3409
+ await persistApiKeyAsync(session.sessionToken, persistedServerUrl);
2999
3410
  if (options.scope !== false) {
3000
- await configureDefaultScopeAsync(authConfig.convexSiteUrl);
3411
+ await configureDefaultScopeAsync(persistedServerUrl);
3001
3412
  }
3002
3413
  log.success(`Session saved to ${CONFIG_FILE}`);
3003
3414
  outro(
@@ -3025,12 +3436,12 @@ async function authCommand(options) {
3025
3436
  const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : DEFAULT_TIMEOUT_SECONDS;
3026
3437
  if (Number.isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
3027
3438
  log.error("Invalid timeout value. Must be a positive number of seconds.");
3028
- process8.exit(1);
3439
+ process10.exit(1);
3029
3440
  }
3030
3441
  await deviceFlow(serverUrl, timeoutSeconds, options);
3031
3442
  }
3032
3443
  async function displayTokenSource(masked) {
3033
- if (process8.env.BRAID_API_KEY) {
3444
+ if (process10.env.BRAID_API_KEY) {
3034
3445
  log.info(`Authenticated with key: ${masked}`);
3035
3446
  log.info("Source: BRAID_API_KEY environment variable");
3036
3447
  return;
@@ -3111,7 +3522,7 @@ async function authStatusCommand() {
3111
3522
  );
3112
3523
  return;
3113
3524
  }
3114
- if (config.token.startsWith(SESSION_TOKEN_PREFIX)) {
3525
+ if (config.token.startsWith(SESSION_TOKEN_PREFIX3)) {
3115
3526
  await displaySessionInfo(config.token);
3116
3527
  await displayProjectConfig();
3117
3528
  displayResolvedSettings(config);
@@ -3134,7 +3545,7 @@ async function revokeSessionIfActive(token) {
3134
3545
  async function authLogoutCommand() {
3135
3546
  const config = await loadMergedConfigAsync();
3136
3547
  const { clearApiKeyAsync: clearApiKeyAsync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3137
- if (config.token?.startsWith(SESSION_TOKEN_PREFIX)) {
3548
+ if (config.token?.startsWith(SESSION_TOKEN_PREFIX3)) {
3138
3549
  await revokeSessionIfActive(config.token);
3139
3550
  }
3140
3551
  await clearApiKeyAsync2();
@@ -3146,7 +3557,6 @@ init_esm_shims();
3146
3557
  init_api();
3147
3558
  init_config();
3148
3559
  init_tui();
3149
- import process9 from "process";
3150
3560
  var parseCsv2 = (input) => {
3151
3561
  if (!input) {
3152
3562
  return void 0;
@@ -3154,15 +3564,6 @@ var parseCsv2 = (input) => {
3154
3564
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3155
3565
  return values.length > 0 ? values : void 0;
3156
3566
  };
3157
- var writeJson2 = (value) => {
3158
- process9.stdout.write(`${JSON.stringify(value, null, 2)}
3159
- `);
3160
- };
3161
- var exitWithError2 = (error) => {
3162
- const message = error instanceof Error ? error.message : String(error);
3163
- log.error(message);
3164
- process9.exit(1);
3165
- };
3166
3567
  var applyCommonOptions = (base, options) => ({
3167
3568
  ...base,
3168
3569
  ...options.server ? { serverUrl: options.server } : {},
@@ -3189,7 +3590,7 @@ async function projectsListCommand(options) {
3189
3590
  applyCommonOptions({}, options)
3190
3591
  );
3191
3592
  if (options.json) {
3192
- writeJson2(result);
3593
+ writeJson(result);
3193
3594
  return;
3194
3595
  }
3195
3596
  log.info("Personal projects:");
@@ -3211,7 +3612,7 @@ Organization: ${orgProjects.orgName} (${orgProjects.orgId})`);
3211
3612
  }
3212
3613
  }
3213
3614
  } catch (error) {
3214
- exitWithError2(error);
3615
+ exitCommandError(error, { json: options.json });
3215
3616
  }
3216
3617
  }
3217
3618
  var buildRulesRequest = async (options) => {
@@ -3232,7 +3633,7 @@ async function rulesListCommand(options) {
3232
3633
  await buildRulesRequest(options)
3233
3634
  );
3234
3635
  if (options.json) {
3235
- writeJson2(result);
3636
+ writeJson(result);
3236
3637
  return;
3237
3638
  }
3238
3639
  if (result.rules.length === 0) {
@@ -3243,7 +3644,7 @@ async function rulesListCommand(options) {
3243
3644
  log.info(`${rule.id} ${rule.title}`);
3244
3645
  }
3245
3646
  } catch (error) {
3246
- exitWithError2(error);
3647
+ exitCommandError(error, { json: options.json });
3247
3648
  }
3248
3649
  }
3249
3650
  var buildSkillsRequest = async (options) => {
@@ -3263,7 +3664,7 @@ async function skillsListCommand(options) {
3263
3664
  try {
3264
3665
  const result = await fetchSkillsAsync(await buildSkillsRequest(options));
3265
3666
  if (options.json) {
3266
- writeJson2(result);
3667
+ writeJson(result);
3267
3668
  return;
3268
3669
  }
3269
3670
  if (result.skills.length === 0) {
@@ -3274,7 +3675,7 @@ async function skillsListCommand(options) {
3274
3675
  log.info(`${skill.name}`);
3275
3676
  }
3276
3677
  } catch (error) {
3277
- exitWithError2(error);
3678
+ exitCommandError(error, { json: options.json });
3278
3679
  }
3279
3680
  }
3280
3681
 
@@ -3421,11 +3822,11 @@ init_lockfile();
3421
3822
  init_esm_shims();
3422
3823
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3423
3824
  import { dirname as dirname6, join as join7 } from "path";
3424
- 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";
3425
3826
  var METADATA_FILENAME2 = ".braid-metadata.json";
3426
- var MetadataReadError = class extends Data5.TaggedError("MetadataReadError") {
3827
+ var MetadataReadError = class extends Data6.TaggedError("MetadataReadError") {
3427
3828
  };
3428
- var MetadataWriteError = class extends Data5.TaggedError("MetadataWriteError") {
3829
+ var MetadataWriteError = class extends Data6.TaggedError("MetadataWriteError") {
3429
3830
  };
3430
3831
  var normalizeInstalledSkill = (skill) => ({
3431
3832
  kind: skill.kind ?? "skill",
@@ -3441,18 +3842,18 @@ var normalizeMetadata = (metadata) => ({
3441
3842
  var getMetadataPath = (skillsDir) => join7(dirname6(skillsDir), METADATA_FILENAME2);
3442
3843
  var readMetadata = (skillsDir) => {
3443
3844
  const metadataPath = getMetadataPath(skillsDir);
3444
- return pipe6(
3445
- Effect6.tryPromise({
3845
+ return pipe7(
3846
+ Effect7.tryPromise({
3446
3847
  try: () => readFile4(metadataPath, "utf-8"),
3447
3848
  catch: (e) => new MetadataReadError({ path: metadataPath, cause: e })
3448
3849
  }),
3449
- Effect6.flatMap(
3450
- (content) => Effect6.try({
3850
+ Effect7.flatMap(
3851
+ (content) => Effect7.try({
3451
3852
  try: () => normalizeMetadata(JSON.parse(content)),
3452
3853
  catch: () => normalizeMetadata(void 0)
3453
3854
  })
3454
3855
  ),
3455
- Effect6.orElseSucceed(() => normalizeMetadata(void 0))
3856
+ Effect7.orElseSucceed(() => normalizeMetadata(void 0))
3456
3857
  );
3457
3858
  };
3458
3859
  var readRawJson = async (path2) => {
@@ -3469,7 +3870,7 @@ var readRawJson = async (path2) => {
3469
3870
  };
3470
3871
  var writeMetadata = (skillsDir, metadata) => {
3471
3872
  const metadataPath = getMetadataPath(skillsDir);
3472
- return Effect6.tryPromise({
3873
+ return Effect7.tryPromise({
3473
3874
  try: async () => {
3474
3875
  const existing = await readRawJson(metadataPath);
3475
3876
  const merged = { ...existing, skills: metadata.skills };
@@ -3478,9 +3879,9 @@ var writeMetadata = (skillsDir, metadata) => {
3478
3879
  catch: (e) => new MetadataWriteError({ path: metadataPath, cause: e })
3479
3880
  });
3480
3881
  };
3481
- var updateMetadata = (skillsDir, newSkills) => pipe6(
3882
+ var updateMetadata = (skillsDir, newSkills) => pipe7(
3482
3883
  readMetadata(skillsDir),
3483
- Effect6.map((existing) => {
3884
+ Effect7.map((existing) => {
3484
3885
  const now = (/* @__PURE__ */ new Date()).toISOString();
3485
3886
  const updatedSkills = [...existing.skills];
3486
3887
  for (const skill of newSkills) {
@@ -3503,35 +3904,35 @@ var updateMetadata = (skillsDir, newSkills) => pipe6(
3503
3904
  }
3504
3905
  return { skills: updatedSkills };
3505
3906
  }),
3506
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3907
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3507
3908
  );
3508
- var removeFromMetadata = (skillsDir, skillName) => pipe6(
3909
+ var removeFromMetadata = (skillsDir, skillName) => pipe7(
3509
3910
  readMetadata(skillsDir),
3510
- Effect6.map((metadata) => ({
3911
+ Effect7.map((metadata) => ({
3511
3912
  skills: metadata.skills.filter((s) => s.name !== skillName)
3512
3913
  })),
3513
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3914
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3514
3915
  );
3515
- var readMetadataAsync = (skillsDir) => Effect6.runPromise(readMetadata(skillsDir));
3516
- var updateMetadataAsync = (skillsDir, newSkills) => Effect6.runPromise(updateMetadata(skillsDir, newSkills));
3517
- 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));
3518
3919
 
3519
3920
  // src/lib/rule-writer.ts
3520
3921
  init_esm_shims();
3521
3922
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
3522
3923
  import { dirname as dirname7, resolve as resolve3, sep as sep2 } from "path";
3523
- import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3524
- 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") {
3525
3926
  };
3526
- var createDirectory = (dir) => Effect7.tryPromise({
3927
+ var createDirectory = (dir) => Effect8.tryPromise({
3527
3928
  try: () => mkdir4(dir, { recursive: true }),
3528
3929
  catch: (e) => new RuleWriteError({ path: dir, operation: "mkdir", cause: e })
3529
3930
  });
3530
- var writeTextFile = (fullPath, content) => Effect7.tryPromise({
3931
+ var writeTextFile = (fullPath, content) => Effect8.tryPromise({
3531
3932
  try: () => writeFile6(fullPath, content, "utf-8"),
3532
3933
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "write", cause: e })
3533
3934
  });
3534
- var readTextFile = (fullPath) => Effect7.tryPromise({
3935
+ var readTextFile = (fullPath) => Effect8.tryPromise({
3535
3936
  try: () => readFile5(fullPath, "utf-8"),
3536
3937
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "read", cause: e })
3537
3938
  });
@@ -3539,7 +3940,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3539
3940
  const resolvedBase = resolve3(basePath);
3540
3941
  const resolvedFull = resolve3(basePath, ruleName);
3541
3942
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep2)) {
3542
- return Effect7.fail(
3943
+ return Effect8.fail(
3543
3944
  new RuleWriteError({
3544
3945
  path: ruleName,
3545
3946
  operation: "write",
@@ -3549,7 +3950,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3549
3950
  })
3550
3951
  );
3551
3952
  }
3552
- return Effect7.succeed(resolvedFull);
3953
+ return Effect8.succeed(resolvedFull);
3553
3954
  };
3554
3955
  var BRAID_SECTION_START = "<!-- braid:rules:start -->";
3555
3956
  var BRAID_SECTION_END = "<!-- braid:rules:end -->";
@@ -3582,30 +3983,30 @@ function buildAppendContent(rules2) {
3582
3983
  lines.push(BRAID_SECTION_END);
3583
3984
  return lines.join("\n");
3584
3985
  }
3585
- var writeMdcRules = (basePath, rules2) => pipe7(
3986
+ var writeMdcRules = (basePath, rules2) => pipe8(
3586
3987
  createDirectory(basePath),
3587
- Effect7.flatMap(
3588
- () => Effect7.forEach(
3988
+ Effect8.flatMap(
3989
+ () => Effect8.forEach(
3589
3990
  rules2,
3590
- (rule) => pipe7(
3991
+ (rule) => pipe8(
3591
3992
  assertRulePathWithinBase(basePath, `${rule.name}.mdc`),
3592
- Effect7.flatMap(
3993
+ Effect8.flatMap(
3593
3994
  (filePath) => writeTextFile(filePath, buildMdcContent(rule))
3594
3995
  )
3595
3996
  ),
3596
3997
  { concurrency: "unbounded" }
3597
3998
  )
3598
3999
  ),
3599
- Effect7.asVoid
4000
+ Effect8.asVoid
3600
4001
  );
3601
- var writeMarkdownDirRules = (basePath, rules2) => pipe7(
4002
+ var writeMarkdownDirRules = (basePath, rules2) => pipe8(
3602
4003
  createDirectory(basePath),
3603
- Effect7.flatMap(
3604
- () => Effect7.forEach(
4004
+ Effect8.flatMap(
4005
+ () => Effect8.forEach(
3605
4006
  rules2,
3606
- (rule) => pipe7(
4007
+ (rule) => pipe8(
3607
4008
  assertRulePathWithinBase(basePath, `${rule.name}.md`),
3608
- Effect7.flatMap(
4009
+ Effect8.flatMap(
3609
4010
  (filePath) => writeTextFile(filePath, `# ${rule.title}
3610
4011
 
3611
4012
  ${rule.content}`)
@@ -3614,17 +4015,17 @@ ${rule.content}`)
3614
4015
  { concurrency: "unbounded" }
3615
4016
  )
3616
4017
  ),
3617
- Effect7.asVoid
4018
+ Effect8.asVoid
3618
4019
  );
3619
- var writeAppendSingleRules = (filePath, rules2) => pipe7(
4020
+ var writeAppendSingleRules = (filePath, rules2) => pipe8(
3620
4021
  createDirectory(dirname7(filePath)),
3621
- Effect7.flatMap(
3622
- () => pipe7(
4022
+ Effect8.flatMap(
4023
+ () => pipe8(
3623
4024
  readTextFile(filePath),
3624
- Effect7.orElseSucceed(() => "")
4025
+ Effect8.orElseSucceed(() => "")
3625
4026
  )
3626
4027
  ),
3627
- Effect7.flatMap((existing) => {
4028
+ Effect8.flatMap((existing) => {
3628
4029
  const braidContent = buildAppendContent(rules2);
3629
4030
  const startIdx = existing.indexOf(BRAID_SECTION_START);
3630
4031
  const endIdx = existing.indexOf(BRAID_SECTION_END);
@@ -3648,21 +4049,21 @@ var writeRulesForFormat = (basePath, rules2, format) => {
3648
4049
  case "append-single":
3649
4050
  return writeAppendSingleRules(basePath, rules2);
3650
4051
  default:
3651
- return Effect7.void;
4052
+ return Effect8.void;
3652
4053
  }
3653
4054
  };
3654
4055
  var writeRulesForAgent = (agent, rules2, rulesPath) => {
3655
4056
  if (!agent.ruleFormat || rules2.length === 0) {
3656
- return Effect7.succeed({ written: 0, errors: [] });
4057
+ return Effect8.succeed({ written: 0, errors: [] });
3657
4058
  }
3658
- return pipe7(
4059
+ return pipe8(
3659
4060
  writeRulesForFormat(rulesPath, rules2, agent.ruleFormat),
3660
- Effect7.map(() => ({
4061
+ Effect8.map(() => ({
3661
4062
  written: rules2.length,
3662
4063
  errors: []
3663
4064
  })),
3664
- Effect7.catch(
3665
- (error) => Effect7.succeed({
4065
+ Effect8.catch(
4066
+ (error) => Effect8.succeed({
3666
4067
  written: 0,
3667
4068
  errors: [
3668
4069
  {
@@ -3674,29 +4075,29 @@ var writeRulesForAgent = (agent, rules2, rulesPath) => {
3674
4075
  )
3675
4076
  );
3676
4077
  };
3677
- var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect7.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
4078
+ var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect8.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
3678
4079
 
3679
4080
  // src/lib/skill-writer.ts
3680
4081
  init_esm_shims();
3681
4082
  import { createHash as createHash2 } from "crypto";
3682
4083
  import { chmod, mkdir as mkdir5, rm as rm2, symlink, writeFile as writeFile7 } from "fs/promises";
3683
4084
  import { dirname as dirname8, join as join8, resolve as resolve4, sep as sep3 } from "path";
3684
- import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3685
- 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") {
3686
4087
  };
3687
- var createDirectory2 = (dir, fullPath) => Effect8.tryPromise({
4088
+ var createDirectory2 = (dir, fullPath) => Effect9.tryPromise({
3688
4089
  try: () => mkdir5(dir, { recursive: true }),
3689
4090
  catch: (e) => new WriteError({ path: fullPath, operation: "mkdir", cause: e })
3690
4091
  });
3691
- var writeTextFile2 = (fullPath, content) => Effect8.tryPromise({
4092
+ var writeTextFile2 = (fullPath, content) => Effect9.tryPromise({
3692
4093
  try: () => writeFile7(fullPath, content, "utf-8"),
3693
4094
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3694
4095
  });
3695
- var writeBinaryFile = (fullPath, content) => Effect8.tryPromise({
4096
+ var writeBinaryFile = (fullPath, content) => Effect9.tryPromise({
3696
4097
  try: () => writeFile7(fullPath, content),
3697
4098
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3698
4099
  });
3699
- var makeExecutable = (fullPath) => Effect8.tryPromise({
4100
+ var makeExecutable = (fullPath) => Effect9.tryPromise({
3700
4101
  try: () => chmod(fullPath, 493),
3701
4102
  catch: (e) => new WriteError({ path: fullPath, operation: "chmod", cause: e })
3702
4103
  });
@@ -3704,7 +4105,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3704
4105
  const resolvedBase = resolve4(basePath);
3705
4106
  const resolvedFull = resolve4(basePath, untrustedPath);
3706
4107
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep3)) {
3707
- return Effect8.fail(
4108
+ return Effect9.fail(
3708
4109
  new WriteError({
3709
4110
  path: untrustedPath,
3710
4111
  operation: "write",
@@ -3714,7 +4115,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3714
4115
  })
3715
4116
  );
3716
4117
  }
3717
- return Effect8.succeed(resolvedFull);
4118
+ return Effect9.succeed(resolvedFull);
3718
4119
  };
3719
4120
  var COMPATIBILITY_REGEX = /^compatibility:\s*.+$/m;
3720
4121
  var rewriteCompatibility = (content, agentId) => {
@@ -3757,22 +4158,22 @@ var isScriptFile = (path2) => {
3757
4158
  return inScriptsDir && scriptExtensions.some((ext) => lowerPath.endsWith(ext));
3758
4159
  };
3759
4160
  var writeFileContent = (fullPath, file, agentId) => isBinaryFile(file.path) ? writeBinaryFile(fullPath, decodeFileContentBinary(file)) : writeTextFile2(fullPath, decodeFileContent(file, agentId));
3760
- var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path) && decodeFileContent(file, agentId).startsWith("#!") ? makeExecutable(fullPath) : Effect8.void;
3761
- 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(
3762
4163
  assertWithinBase2(basePath, file.path),
3763
- Effect8.flatMap((fullPath) => {
4164
+ Effect9.flatMap((fullPath) => {
3764
4165
  const dir = dirname8(fullPath);
3765
- return pipe8(
4166
+ return pipe9(
3766
4167
  createDirectory2(dir, fullPath),
3767
- Effect8.flatMap(() => writeFileContent(fullPath, file, agentId)),
3768
- Effect8.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
4168
+ Effect9.flatMap(() => writeFileContent(fullPath, file, agentId)),
4169
+ Effect9.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
3769
4170
  );
3770
4171
  })
3771
4172
  );
3772
- var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
4173
+ var writeSkillAtPath = (skillPath, skill, agentId) => pipe9(
3773
4174
  createDirectory2(skillPath, skillPath),
3774
- Effect8.flatMap(
3775
- () => Effect8.forEach(
4175
+ Effect9.flatMap(
4176
+ () => Effect9.forEach(
3776
4177
  skill.files,
3777
4178
  (file) => writeSkillFile(skillPath, file, agentId),
3778
4179
  {
@@ -3780,16 +4181,16 @@ var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
3780
4181
  }
3781
4182
  )
3782
4183
  ),
3783
- Effect8.map(() => void 0)
4184
+ Effect9.map(() => void 0)
3784
4185
  );
3785
- var writeSkill = (basePath, skill, agentId) => pipe8(
4186
+ var writeSkill = (basePath, skill, agentId) => pipe9(
3786
4187
  assertWithinBase2(basePath, skill.name),
3787
- Effect8.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
4188
+ Effect9.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
3788
4189
  );
3789
- var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
4190
+ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe9(
3790
4191
  assertWithinBase2(basePath, skill.name),
3791
- Effect8.flatMap(
3792
- (installPath) => Effect8.tryPromise({
4192
+ Effect9.flatMap(
4193
+ (installPath) => Effect9.tryPromise({
3793
4194
  try: async () => {
3794
4195
  if (!options.storeRoot) {
3795
4196
  throw new Error("storeRoot is required for shared-store installs");
@@ -3799,7 +4200,7 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3799
4200
  skill,
3800
4201
  agentId
3801
4202
  );
3802
- await Effect8.runPromise(
4203
+ await Effect9.runPromise(
3803
4204
  writeSkillAtPath(storedBundlePath, skill, agentId)
3804
4205
  );
3805
4206
  const destinationPath = await resolveManagedBundlePathAsync(
@@ -3826,14 +4227,14 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3826
4227
  );
3827
4228
  var hashSkill = (skill, agentId) => createHash2("sha256").update(JSON.stringify({ agentId, skill })).digest("hex").slice(0, 16);
3828
4229
  var resolveStorePath = (storeRoot, skill, agentId) => join8(storeRoot, `${skill.name}-${hashSkill(skill, agentId)}`);
3829
- var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3830
- Effect8.forEach(
4230
+ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe9(
4231
+ Effect9.forEach(
3831
4232
  skills2,
3832
- (skill) => pipe8(
4233
+ (skill) => pipe9(
3833
4234
  options.storeRoot ? writeSkillSymlink(basePath, skill, agentId, options) : writeSkill(basePath, skill, agentId),
3834
- Effect8.map(() => ({ success: true, skill: skill.name })),
3835
- Effect8.catch(
3836
- (error) => Effect8.succeed({
4235
+ Effect9.map(() => ({ success: true, skill: skill.name })),
4236
+ Effect9.catch(
4237
+ (error) => Effect9.succeed({
3837
4238
  success: false,
3838
4239
  skill: skill.name,
3839
4240
  error: error.cause instanceof Error ? error.cause.message : String(error.cause)
@@ -3842,14 +4243,14 @@ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3842
4243
  ),
3843
4244
  { concurrency: "unbounded" }
3844
4245
  ),
3845
- Effect8.map((results) => ({
4246
+ Effect9.map((results) => ({
3846
4247
  written: results.filter((r) => r.success).map((r) => r.skill),
3847
4248
  errors: results.filter(
3848
4249
  (r) => !r.success
3849
4250
  ).map((r) => ({ skill: r.skill, error: r.error }))
3850
4251
  }))
3851
4252
  );
3852
- 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));
3853
4254
 
3854
4255
  // src/commands/install.ts
3855
4256
  init_tui();
@@ -4299,6 +4700,7 @@ async function writeLockfileAfterInstall(handle, slug, version, contentHash, rul
4299
4700
  }
4300
4701
  }
4301
4702
  async function publicInstallCommand(source, options) {
4703
+ const config = await loadMergedConfigAsync();
4302
4704
  const parsed = parsePublicSource(source);
4303
4705
  if (!parsed) {
4304
4706
  log.error(`Invalid public source: ${source}`);
@@ -4322,6 +4724,7 @@ async function publicInstallCommand(source, options) {
4322
4724
  log.info(`Using locked version: v${version}`);
4323
4725
  }
4324
4726
  const versionSuffix = version !== void 0 ? `@${version}` : "";
4727
+ const serverUrl = options.server ?? config.serverUrl;
4325
4728
  intro(`Installing from @${handle}/${slug}${versionSuffix}`);
4326
4729
  const installSpinner = spinner();
4327
4730
  installSpinner.start("Fetching public content...");
@@ -4331,7 +4734,10 @@ async function publicInstallCommand(source, options) {
4331
4734
  slug,
4332
4735
  version,
4333
4736
  lockedRuleIds,
4334
- options,
4737
+ {
4738
+ ...options,
4739
+ server: serverUrl
4740
+ },
4335
4741
  installSpinner
4336
4742
  );
4337
4743
  installSpinner.stop(`Downloaded ${summarizeContent(response)}`);
@@ -4339,11 +4745,13 @@ async function publicInstallCommand(source, options) {
4339
4745
  log.warn("No content to install.");
4340
4746
  process.exit(0);
4341
4747
  }
4342
- const serverUrl = options.server ?? "https://braid.cloud";
4343
4748
  const { totalWritten, totalErrors } = await installToSelectedAgents(
4344
4749
  response,
4345
4750
  serverUrl,
4346
- options,
4751
+ {
4752
+ ...options,
4753
+ server: serverUrl
4754
+ },
4347
4755
  installSpinner
4348
4756
  );
4349
4757
  if (totalWritten > 0 && version !== void 0) {
@@ -4540,7 +4948,8 @@ async function listCommand(options) {
4540
4948
 
4541
4949
  // src/commands/manage.ts
4542
4950
  init_esm_shims();
4543
- import process10 from "process";
4951
+ init_device_auth();
4952
+ import process11 from "process";
4544
4953
 
4545
4954
  // src/lib/manage-server.ts
4546
4955
  init_esm_shims();
@@ -4550,6 +4959,7 @@ import { createServer } from "http";
4550
4959
  import { isAbsolute, relative as relative2, resolve as resolve6 } from "path";
4551
4960
  import { fileURLToPath as fileURLToPath2 } from "url";
4552
4961
  init_config();
4962
+ init_device_auth();
4553
4963
 
4554
4964
  // src/lib/manage-actions.ts
4555
4965
  init_esm_shims();
@@ -4560,8 +4970,8 @@ import { basename as basename2, dirname as dirname10, join as join11, relative }
4560
4970
  init_esm_shims();
4561
4971
  import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile8 } from "fs/promises";
4562
4972
  import { dirname as dirname9, join as join9 } from "path";
4563
- import { Data as Data8, Effect as Effect9 } from "effect";
4564
- 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") {
4565
4975
  };
4566
4976
  var HOOK_METADATA_FILENAME = ".braid-metadata.json";
4567
4977
  function stableStringify(value) {
@@ -4886,7 +5296,7 @@ async function ensureMarketplaceHookBundleFile(settingsPath, sourceSlug) {
4886
5296
  });
4887
5297
  return bundlePath;
4888
5298
  }
4889
- var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise({
5299
+ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect10.tryPromise({
4890
5300
  try: async () => {
4891
5301
  const activeHooks = hooks.filter(
4892
5302
  (hook) => hook.enabled !== false && hook.runtime === "claude"
@@ -4941,131 +5351,18 @@ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise
4941
5351
  },
4942
5352
  catch: (cause) => new HookConfigWriteError({ path: settingsPath, cause })
4943
5353
  });
4944
- var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect9.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
5354
+ var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect10.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
4945
5355
  var removeMarketplaceHooksBySourceAsync = (settingsPath, sourceSlug) => removeMarketplaceHooksBySource(settingsPath, sourceSlug);
4946
5356
  var ensureMarketplaceHookBundleFileAsync = (settingsPath, sourceSlug) => ensureMarketplaceHookBundleFile(settingsPath, sourceSlug);
4947
5357
  var listInstalledMarketplaceHookSourcesAsync = (settingsPath) => listInstalledMarketplaceHookSources(settingsPath);
4948
5358
  var readMarketplaceHookBundleFileAsync = (bundlePath) => readMarketplaceHookBundleFile(bundlePath);
4949
5359
 
4950
- // src/lib/marketplace-api.ts
4951
- init_esm_shims();
4952
- init_config();
4953
- import { Data as Data9, Effect as Effect10, pipe as pipe9 } from "effect";
4954
- var TRAILING_SLASH_REGEX2 = /\/$/;
4955
- var MarketplaceApiError = class extends Data9.TaggedError("MarketplaceApiError") {
4956
- };
4957
- var resolveApiKey2 = (provided) => {
4958
- if (provided) {
4959
- return Effect10.succeed(provided);
4960
- }
4961
- return pipe9(
4962
- Effect10.tryPromise({
4963
- try: () => getApiKeyAsync(),
4964
- catch: () => new MarketplaceApiError({
4965
- message: "Failed to load API key",
4966
- status: 500,
4967
- code: "CONFIG_ERROR"
4968
- })
4969
- }),
4970
- Effect10.flatMap(
4971
- (token) => token ? Effect10.succeed(token) : Effect10.fail(
4972
- new MarketplaceApiError({
4973
- message: "Not signed in. Run 'braid auth'.",
4974
- status: 401,
4975
- code: "AUTH_REQUIRED"
4976
- })
4977
- )
4978
- )
4979
- );
4980
- };
4981
- var resolveServer = (provided) => {
4982
- if (provided) {
4983
- return Effect10.succeed(provided.replace(TRAILING_SLASH_REGEX2, ""));
4984
- }
4985
- return pipe9(
4986
- Effect10.tryPromise({
4987
- try: () => getServerUrlAsync(),
4988
- catch: () => new MarketplaceApiError({
4989
- message: "Failed to load server URL",
4990
- status: 500,
4991
- code: "CONFIG_ERROR"
4992
- })
4993
- }),
4994
- Effect10.map((server) => server.replace(TRAILING_SLASH_REGEX2, ""))
4995
- );
4996
- };
4997
- var requestJson = (url, options) => pipe9(
4998
- Effect10.tryPromise({
4999
- try: () => fetch(url, options),
5000
- catch: () => new MarketplaceApiError({
5001
- message: "Network error",
5002
- status: 503,
5003
- code: "NETWORK_ERROR"
5004
- })
5005
- }),
5006
- Effect10.flatMap(
5007
- (response) => Effect10.tryPromise({
5008
- try: async () => {
5009
- const payload = await response.json().catch(() => null);
5010
- if (!response.ok) {
5011
- throw new MarketplaceApiError({
5012
- message: payload?.error ?? payload?.message ?? `Request failed (HTTP ${response.status} from ${new URL(url).origin})`,
5013
- status: response.status,
5014
- code: payload?.code ?? "REQUEST_ERROR"
5015
- });
5016
- }
5017
- return payload;
5018
- },
5019
- catch: (error) => error instanceof MarketplaceApiError ? error : new MarketplaceApiError({
5020
- message: "Invalid response",
5021
- status: 500,
5022
- code: "PARSE_ERROR"
5023
- })
5024
- })
5025
- )
5026
- );
5027
- var fetchMarketplaceLibrary = (options) => pipe9(
5028
- Effect10.all({
5029
- server: resolveServer(options.server),
5030
- apiKey: resolveApiKey2(options.apiKey)
5031
- }),
5032
- Effect10.flatMap(
5033
- ({ server, apiKey }) => requestJson(
5034
- `${server}/api/marketplace/library`,
5035
- {
5036
- method: "GET",
5037
- headers: {
5038
- Authorization: `Bearer ${apiKey}`
5039
- }
5040
- }
5041
- )
5042
- ),
5043
- Effect10.map((response) => response.items)
5044
- );
5045
- var fetchMarketplaceInstallManifest = (slug, options) => pipe9(
5046
- Effect10.all({
5047
- server: resolveServer(options.server),
5048
- apiKey: resolveApiKey2(options.apiKey)
5049
- }),
5050
- Effect10.flatMap(
5051
- ({ server, apiKey }) => requestJson(
5052
- `${server}/api/marketplace/install-manifest/${encodeURIComponent(slug)}`,
5053
- {
5054
- method: "GET",
5055
- headers: {
5056
- Authorization: `Bearer ${apiKey}`
5057
- }
5058
- }
5059
- )
5060
- )
5061
- );
5062
- var fetchMarketplaceLibraryAsync = (options) => Effect10.runPromise(fetchMarketplaceLibrary(options));
5063
- var fetchMarketplaceInstallManifestAsync = (slug, options) => Effect10.runPromise(fetchMarketplaceInstallManifest(slug, options));
5064
-
5065
5360
  // src/lib/marketplace-installer.ts
5066
5361
  init_esm_shims();
5067
5362
  import { rm as rm4 } from "fs/promises";
5068
5363
  import { join as join10 } from "path";
5364
+ init_config();
5365
+ var TRAILING_SLASH_REGEX3 = /\/$/;
5069
5366
  function parseAgentIds(value) {
5070
5367
  if (!value) {
5071
5368
  return [];
@@ -5135,7 +5432,7 @@ async function installSkillsForAgent(args) {
5135
5432
  versionId: args.manifest.versionId
5136
5433
  },
5137
5434
  version: args.manifest.versionId ?? args.manifest.commitSha ?? "unknown",
5138
- serverUrl: "https://braid.cloud"
5435
+ serverUrl: args.serverUrl
5139
5436
  }))
5140
5437
  );
5141
5438
  args.targets.push(`${args.agent.name} -> ${installPath}`);
@@ -5224,6 +5521,7 @@ async function installAllForAgent(args) {
5224
5521
  agent: args.agent,
5225
5522
  manifest: args.manifest,
5226
5523
  slug: args.slug,
5524
+ serverUrl: args.serverUrl,
5227
5525
  skills: args.skills,
5228
5526
  global: args.global,
5229
5527
  installedSkills: args.installedSkills,
@@ -5281,6 +5579,7 @@ async function installMarketplaceSkillSet(options) {
5281
5579
  "No compatible agents found. Use --agents to specify targets."
5282
5580
  );
5283
5581
  }
5582
+ const serverUrl = (options.serverUrl ?? await getServerUrlAsync().catch(() => "https://braid.cloud")).replace(TRAILING_SLASH_REGEX3, "");
5284
5583
  const targets = [];
5285
5584
  const installedSkills = /* @__PURE__ */ new Set();
5286
5585
  for (const agent of targetAgents) {
@@ -5288,6 +5587,7 @@ async function installMarketplaceSkillSet(options) {
5288
5587
  agent,
5289
5588
  manifest: options.manifest,
5290
5589
  slug: options.slug,
5590
+ serverUrl,
5291
5591
  skills: resolvedSkills,
5292
5592
  rules: rules2,
5293
5593
  agents: agents2,
@@ -5437,7 +5737,8 @@ async function installLibraryPackAsync(options) {
5437
5737
  agents: options.agentId,
5438
5738
  global: options.scope === "global",
5439
5739
  manifest,
5440
- slug: options.slug
5740
+ slug: options.slug,
5741
+ serverUrl: options.server
5441
5742
  });
5442
5743
  }
5443
5744
 
@@ -5914,7 +6215,8 @@ var MIME_TYPES = {
5914
6215
  };
5915
6216
  var TRAILING_SLASHES3 = /\/+$/;
5916
6217
  var MANAGE_AUTH_TIMEOUT_SECONDS = 300;
5917
- var SESSION_TOKEN_PREFIX2 = "brs_";
6218
+ var SESSION_TOKEN_PREFIX4 = "brs_";
6219
+ var LOOPBACK_HOSTS4 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
5918
6220
  function getErrorStatus(error) {
5919
6221
  if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") {
5920
6222
  return error.status ?? 500;
@@ -5991,7 +6293,18 @@ function getContentType(pathname) {
5991
6293
  const extension = pathname.slice(pathname.lastIndexOf("."));
5992
6294
  return MIME_TYPES[extension] ?? "application/octet-stream";
5993
6295
  }
5994
- 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) {
5995
6308
  response.writeHead(status, {
5996
6309
  "content-type": "application/json; charset=utf-8",
5997
6310
  "cache-control": "no-store"
@@ -6015,7 +6328,7 @@ async function serveStaticAsset(serverResponse, assetRoot, pathname) {
6015
6328
  const assetPath = resolve6(assetRoot, relativePath);
6016
6329
  const resolvedRoot = resolve6(assetRoot);
6017
6330
  if (!isWithinManageAssetRoot(resolvedRoot, assetPath)) {
6018
- writeJson3(serverResponse, 404, { error: "Not found" });
6331
+ writeJson2(serverResponse, 404, { error: "Not found" });
6019
6332
  return true;
6020
6333
  }
6021
6334
  try {
@@ -6109,19 +6422,19 @@ async function handleManageApiRoute(request, response, dependencies) {
6109
6422
  const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
6110
6423
  switch (`${method} ${pathname}`) {
6111
6424
  case "GET /api/inventory":
6112
- writeJson3(response, 200, await dependencies.getInventory());
6425
+ writeJson2(response, 200, await dependencies.getInventory());
6113
6426
  return true;
6114
6427
  case "GET /api/library":
6115
- writeJson3(response, 200, await dependencies.listLibrary());
6428
+ writeJson2(response, 200, await dependencies.listLibrary());
6116
6429
  return true;
6117
6430
  case "GET /api/auth/session":
6118
- writeJson3(response, 200, {
6431
+ writeJson2(response, 200, {
6119
6432
  ok: true,
6120
6433
  result: await dependencies.getAuthSession()
6121
6434
  });
6122
6435
  return true;
6123
6436
  case "POST /api/auth/start":
6124
- writeJson3(response, 200, {
6437
+ writeJson2(response, 200, {
6125
6438
  ok: true,
6126
6439
  result: await dependencies.startAuth()
6127
6440
  });
@@ -6135,7 +6448,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6135
6448
  "AUTH_REQUEST_REQUIRED"
6136
6449
  );
6137
6450
  }
6138
- writeJson3(response, 200, {
6451
+ writeJson2(response, 200, {
6139
6452
  ok: true,
6140
6453
  result: await dependencies.completeAuth({
6141
6454
  requestId: payload.requestId
@@ -6144,14 +6457,14 @@ async function handleManageApiRoute(request, response, dependencies) {
6144
6457
  return true;
6145
6458
  }
6146
6459
  case "POST /api/auth/sign-out":
6147
- writeJson3(response, 200, {
6460
+ writeJson2(response, 200, {
6148
6461
  ok: true,
6149
6462
  result: await dependencies.signOut()
6150
6463
  });
6151
6464
  return true;
6152
6465
  case "POST /api/actions/install": {
6153
6466
  const payload = await readJsonBody(request);
6154
- writeJson3(response, 200, {
6467
+ writeJson2(response, 200, {
6155
6468
  ok: true,
6156
6469
  result: await dependencies.installLibraryPack(payload)
6157
6470
  });
@@ -6159,7 +6472,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6159
6472
  }
6160
6473
  case "POST /api/actions/disable": {
6161
6474
  const payload = await readJsonBody(request);
6162
- writeJson3(response, 200, {
6475
+ writeJson2(response, 200, {
6163
6476
  ok: true,
6164
6477
  result: await dependencies.disableManagedBundle(payload)
6165
6478
  });
@@ -6167,7 +6480,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6167
6480
  }
6168
6481
  case "POST /api/actions/enable": {
6169
6482
  const payload = await readJsonBody(request);
6170
- writeJson3(response, 200, {
6483
+ writeJson2(response, 200, {
6171
6484
  ok: true,
6172
6485
  result: await dependencies.enableManagedBundle(payload)
6173
6486
  });
@@ -6175,7 +6488,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6175
6488
  }
6176
6489
  case "POST /api/actions/remove": {
6177
6490
  const payload = await readJsonBody(request);
6178
- writeJson3(response, 200, {
6491
+ writeJson2(response, 200, {
6179
6492
  ok: true,
6180
6493
  result: await dependencies.removeManagedBundle(payload)
6181
6494
  });
@@ -6197,11 +6510,11 @@ async function handleManageRequest(request, response, assetRoot, dependencies) {
6197
6510
  return;
6198
6511
  }
6199
6512
  }
6200
- writeJson3(response, 404, { error: "Not found" });
6513
+ writeJson2(response, 404, { error: "Not found" });
6201
6514
  } catch (error) {
6202
6515
  const message = error instanceof Error ? error.message : String(error);
6203
6516
  const code = getErrorCode(error);
6204
- writeJson3(response, getErrorStatus(error), {
6517
+ writeJson2(response, getErrorStatus(error), {
6205
6518
  ...code ? { code } : {},
6206
6519
  error: message
6207
6520
  });
@@ -6272,6 +6585,10 @@ async function startManageServer(options = {}) {
6272
6585
  deviceAuth.user_code
6273
6586
  );
6274
6587
  authRequests.set(requestId, {
6588
+ serverUrl: resolvePersistedCliServerUrl2(
6589
+ serverUrl,
6590
+ authConfig.convexSiteUrl
6591
+ ),
6275
6592
  convexSiteUrl: authConfig.convexSiteUrl,
6276
6593
  deviceCode: deviceAuth.device_code,
6277
6594
  expiresIn: deviceAuth.expires_in,
@@ -6304,7 +6621,7 @@ async function startManageServer(options = {}) {
6304
6621
  );
6305
6622
  await persistApiKeyAsync(
6306
6623
  session.sessionToken,
6307
- pendingRequest.convexSiteUrl
6624
+ pendingRequest.serverUrl
6308
6625
  );
6309
6626
  return {
6310
6627
  authenticated: true,
@@ -6321,7 +6638,7 @@ async function startManageServer(options = {}) {
6321
6638
  if (!token) {
6322
6639
  return { authenticated: false };
6323
6640
  }
6324
- if (!token.startsWith(SESSION_TOKEN_PREFIX2)) {
6641
+ if (!token.startsWith(SESSION_TOKEN_PREFIX4)) {
6325
6642
  return { authenticated: true };
6326
6643
  }
6327
6644
  const serverUrl = (options.server ?? config.serverUrl).replace(
@@ -6346,7 +6663,7 @@ async function startManageServer(options = {}) {
6346
6663
  const signOut = options.signOut ?? (async () => {
6347
6664
  const config = await loadMergedConfigAsync();
6348
6665
  const token = config.token;
6349
- if (token?.startsWith(SESSION_TOKEN_PREFIX2)) {
6666
+ if (token?.startsWith(SESSION_TOKEN_PREFIX4)) {
6350
6667
  try {
6351
6668
  const serverUrl = (options.server ?? config.serverUrl).replace(
6352
6669
  TRAILING_SLASHES3,
@@ -6378,7 +6695,7 @@ async function startManageServer(options = {}) {
6378
6695
  handleManageRequest(request, response, assetRoot, dependencies).catch(
6379
6696
  (error) => {
6380
6697
  const message = error instanceof Error ? error.message : String(error);
6381
- writeJson3(response, 500, { error: message });
6698
+ writeJson2(response, 500, { error: message });
6382
6699
  }
6383
6700
  );
6384
6701
  });
@@ -6422,7 +6739,7 @@ async function manageCommand(options) {
6422
6739
  const server = await startManageServer({
6423
6740
  apiKey: options.apiKey,
6424
6741
  port: parsePort(options.port),
6425
- projectRoot: process10.cwd(),
6742
+ projectRoot: process11.cwd(),
6426
6743
  server: options.server
6427
6744
  });
6428
6745
  if (options.open !== false) {
@@ -6432,17 +6749,17 @@ async function manageCommand(options) {
6432
6749
  log.info("Press Ctrl+C to stop.");
6433
6750
  await new Promise((resolveClose, rejectClose) => {
6434
6751
  const shutdown = () => {
6435
- process10.off("SIGINT", shutdown);
6436
- process10.off("SIGTERM", shutdown);
6752
+ process11.off("SIGINT", shutdown);
6753
+ process11.off("SIGTERM", shutdown);
6437
6754
  server.close().then(resolveClose).catch(rejectClose);
6438
6755
  };
6439
- process10.once("SIGINT", shutdown);
6440
- process10.once("SIGTERM", shutdown);
6756
+ process11.once("SIGINT", shutdown);
6757
+ process11.once("SIGTERM", shutdown);
6441
6758
  });
6442
6759
  } catch (error) {
6443
6760
  const message = error instanceof Error ? error.message : String(error);
6444
6761
  log.error(message);
6445
- process10.exit(1);
6762
+ process11.exit(1);
6446
6763
  }
6447
6764
  }
6448
6765
 
@@ -6450,7 +6767,7 @@ async function manageCommand(options) {
6450
6767
  init_esm_shims();
6451
6768
  import { rm as rm6 } from "fs/promises";
6452
6769
  import { join as join13, resolve as resolve7 } from "path";
6453
- import process11 from "process";
6770
+ import process12 from "process";
6454
6771
  init_tui();
6455
6772
  async function selectInstallScope(options) {
6456
6773
  if (options.global != null) {
@@ -6469,7 +6786,7 @@ async function selectInstallScope(options) {
6469
6786
  });
6470
6787
  if (isCancel(result)) {
6471
6788
  cancel("Install cancelled.");
6472
- process11.exit(0);
6789
+ process12.exit(0);
6473
6790
  }
6474
6791
  return result;
6475
6792
  }
@@ -6504,7 +6821,7 @@ async function selectInstallAgents(options, global) {
6504
6821
  });
6505
6822
  if (isCancel(selected)) {
6506
6823
  cancel("Install cancelled.");
6507
- process11.exit(0);
6824
+ process12.exit(0);
6508
6825
  }
6509
6826
  return selected;
6510
6827
  }
@@ -6521,7 +6838,7 @@ async function confirmHooksOptIn(hookCount, options) {
6521
6838
  });
6522
6839
  if (isCancel(result)) {
6523
6840
  cancel("Install cancelled.");
6524
- process11.exit(0);
6841
+ process12.exit(0);
6525
6842
  }
6526
6843
  return result;
6527
6844
  }
@@ -6546,7 +6863,7 @@ async function marketplaceLibraryCommand(options) {
6546
6863
  log.error(
6547
6864
  error instanceof Error ? error.message : "Failed to fetch library"
6548
6865
  );
6549
- process11.exit(1);
6866
+ process12.exit(1);
6550
6867
  }
6551
6868
  }
6552
6869
  async function resolveAndInstallPack(slug, options, overrides) {
@@ -6569,7 +6886,8 @@ async function resolveAndInstallPack(slug, options, overrides) {
6569
6886
  manifest,
6570
6887
  agents: agents2,
6571
6888
  global,
6572
- allowHooks: options.allowHooks
6889
+ allowHooks: options.allowHooks,
6890
+ serverUrl: options.server
6573
6891
  });
6574
6892
  }
6575
6893
  async function marketplaceInstallCommand(slug, options) {
@@ -6580,7 +6898,7 @@ async function marketplaceInstallCommand(slug, options) {
6580
6898
  log.error(
6581
6899
  error instanceof Error ? error.message : "Failed to install pack"
6582
6900
  );
6583
- process11.exit(1);
6901
+ process12.exit(1);
6584
6902
  }
6585
6903
  }
6586
6904
  function addSkillToPack(packMap, slug, versionId, skillName, agent, installPath) {
@@ -6634,7 +6952,7 @@ async function marketplaceUpdateCommand(slug, options) {
6634
6952
  const toUpdate = slug ? installed.filter((p) => p.slug === slug) : installed;
6635
6953
  if (toUpdate.length === 0) {
6636
6954
  log.error(`Pack "${slug}" is not installed.`);
6637
- process11.exit(1);
6955
+ process12.exit(1);
6638
6956
  }
6639
6957
  let updated = 0;
6640
6958
  for (const pack of toUpdate) {
@@ -6654,7 +6972,7 @@ async function marketplaceUpdateCommand(slug, options) {
6654
6972
  log.error(
6655
6973
  error instanceof Error ? error.message : "Failed to update packs"
6656
6974
  );
6657
- process11.exit(1);
6975
+ process12.exit(1);
6658
6976
  }
6659
6977
  }
6660
6978
  async function selectPacksToRemove(installed, slug, options) {
@@ -6680,7 +6998,7 @@ async function selectPacksToRemove(installed, slug, options) {
6680
6998
  });
6681
6999
  if (isCancel(selected)) {
6682
7000
  cancel("Remove cancelled.");
6683
- process11.exit(0);
7001
+ process12.exit(0);
6684
7002
  }
6685
7003
  return selected;
6686
7004
  }
@@ -6718,7 +7036,7 @@ async function marketplaceRemoveCommand(slug, options) {
6718
7036
  });
6719
7037
  if (isCancel(confirmed) || !confirmed) {
6720
7038
  cancel("Remove cancelled.");
6721
- process11.exit(0);
7039
+ process12.exit(0);
6722
7040
  }
6723
7041
  }
6724
7042
  for (const pack of toRemove) {
@@ -6729,7 +7047,7 @@ async function marketplaceRemoveCommand(slug, options) {
6729
7047
  log.error(
6730
7048
  error instanceof Error ? error.message : "Failed to remove packs"
6731
7049
  );
6732
- process11.exit(1);
7050
+ process12.exit(1);
6733
7051
  }
6734
7052
  }
6735
7053
 
@@ -6737,19 +7055,6 @@ async function marketplaceRemoveCommand(slug, options) {
6737
7055
  init_esm_shims();
6738
7056
  init_api();
6739
7057
  init_tui();
6740
- import process12 from "process";
6741
- var writeJson4 = (value) => {
6742
- process12.stdout.write(`${JSON.stringify(value, null, 2)}
6743
- `);
6744
- };
6745
- var exitWithError3 = (error) => {
6746
- const message = error instanceof Error ? error.message : String(error);
6747
- log.error(message);
6748
- process12.exit(1);
6749
- };
6750
- var fail2 = (message) => {
6751
- throw new Error(message);
6752
- };
6753
7058
  var parseContext = (contextJson) => {
6754
7059
  if (!contextJson) {
6755
7060
  return void 0;
@@ -6770,7 +7075,7 @@ var run2 = async (command, args, options) => {
6770
7075
  apiOptions
6771
7076
  );
6772
7077
  if (options.json) {
6773
- writeJson4(result);
7078
+ writeJson(result);
6774
7079
  return;
6775
7080
  }
6776
7081
  log.success(`profiles ${command} completed`);
@@ -6779,34 +7084,34 @@ async function profilesListCommand(options) {
6779
7084
  try {
6780
7085
  await run2("list", {}, options);
6781
7086
  } catch (error) {
6782
- exitWithError3(error);
7087
+ exitCommandError(error, options);
6783
7088
  }
6784
7089
  }
6785
7090
  async function profilesGetCommand(options) {
6786
7091
  try {
6787
7092
  if (!(options.id || options.name)) {
6788
- fail2("profiles get requires --id or --name");
7093
+ fail("profiles get requires --id or --name");
6789
7094
  }
6790
7095
  await run2("get", { id: options.id, name: options.name }, options);
6791
7096
  } catch (error) {
6792
- exitWithError3(error);
7097
+ exitCommandError(error, options);
6793
7098
  }
6794
7099
  }
6795
7100
  async function profilesCreateCommand(options) {
6796
7101
  try {
6797
- const name = options.name ?? fail2("profiles create requires --name");
7102
+ const name = options.name ?? fail("profiles create requires --name");
6798
7103
  await run2(
6799
7104
  "create",
6800
7105
  { name, context: parseContext(options.contextJson) },
6801
7106
  options
6802
7107
  );
6803
7108
  } catch (error) {
6804
- exitWithError3(error);
7109
+ exitCommandError(error, options);
6805
7110
  }
6806
7111
  }
6807
7112
  async function profilesUpdateCommand(options) {
6808
7113
  try {
6809
- const id = options.id ?? fail2("profiles update requires --id");
7114
+ const id = options.id ?? fail("profiles update requires --id");
6810
7115
  await run2(
6811
7116
  "update",
6812
7117
  {
@@ -6817,26 +7122,26 @@ async function profilesUpdateCommand(options) {
6817
7122
  options
6818
7123
  );
6819
7124
  } catch (error) {
6820
- exitWithError3(error);
7125
+ exitCommandError(error, options);
6821
7126
  }
6822
7127
  }
6823
7128
  async function profilesRemoveCommand(options) {
6824
7129
  try {
6825
- const id = options.id ?? fail2("profiles remove requires --id");
7130
+ const id = options.id ?? fail("profiles remove requires --id");
6826
7131
  if (!options.yes) {
6827
- fail2("profiles remove requires --yes");
7132
+ fail("profiles remove requires --yes");
6828
7133
  }
6829
7134
  await run2("remove", { id, yes: true }, options);
6830
7135
  } catch (error) {
6831
- exitWithError3(error);
7136
+ exitCommandError(error, options);
6832
7137
  }
6833
7138
  }
6834
7139
  async function profilesSetDefaultCommand(options) {
6835
7140
  try {
6836
- const id = options.id ?? fail2("profiles set-default requires --id");
7141
+ const id = options.id ?? fail("profiles set-default requires --id");
6837
7142
  await run2("set-default", { id }, options);
6838
7143
  } catch (error) {
6839
- exitWithError3(error);
7144
+ exitCommandError(error, options);
6840
7145
  }
6841
7146
  }
6842
7147
 
@@ -6844,14 +7149,6 @@ async function profilesSetDefaultCommand(options) {
6844
7149
  init_esm_shims();
6845
7150
  init_api();
6846
7151
  init_tui();
6847
- import process13 from "process";
6848
- var writeJson5 = (value) => {
6849
- process13.stdout.write(`${JSON.stringify(value, null, 2)}
6850
- `);
6851
- };
6852
- var fail3 = (message) => {
6853
- throw new Error(message);
6854
- };
6855
7152
  var run3 = async (command, args, options) => {
6856
7153
  const apiOptions = {
6857
7154
  ...options.server ? { serverUrl: options.server } : {},
@@ -6866,39 +7163,34 @@ var run3 = async (command, args, options) => {
6866
7163
  apiOptions
6867
7164
  );
6868
7165
  if (options.json) {
6869
- writeJson5(result);
7166
+ writeJson(result);
6870
7167
  return;
6871
7168
  }
6872
7169
  log.success(`projects ${command} completed`);
6873
7170
  };
6874
- var exitWithError4 = (error) => {
6875
- const message = error instanceof Error ? error.message : String(error);
6876
- log.error(message);
6877
- process13.exit(1);
6878
- };
6879
7171
  async function projectsGetCommand(options) {
6880
7172
  try {
6881
- const id = options.id ?? fail3("projects get requires --id");
7173
+ const id = options.id ?? fail("projects get requires --id");
6882
7174
  await run3("get", { id }, options);
6883
7175
  } catch (error) {
6884
- exitWithError4(error);
7176
+ exitCommandError(error, options);
6885
7177
  }
6886
7178
  }
6887
7179
  async function projectsCreateCommand(options) {
6888
7180
  try {
6889
- const name = options.name ?? fail3("projects create requires --name");
7181
+ const name = options.name ?? fail("projects create requires --name");
6890
7182
  await run3(
6891
7183
  "create",
6892
7184
  { name, description: options.description, orgId: options.orgId },
6893
7185
  options
6894
7186
  );
6895
7187
  } catch (error) {
6896
- exitWithError4(error);
7188
+ exitCommandError(error, options);
6897
7189
  }
6898
7190
  }
6899
7191
  async function projectsUpdateCommand(options) {
6900
7192
  try {
6901
- const id = options.id ?? fail3("projects update requires --id");
7193
+ const id = options.id ?? fail("projects update requires --id");
6902
7194
  await run3(
6903
7195
  "update",
6904
7196
  {
@@ -6909,18 +7201,18 @@ async function projectsUpdateCommand(options) {
6909
7201
  options
6910
7202
  );
6911
7203
  } catch (error) {
6912
- exitWithError4(error);
7204
+ exitCommandError(error, options);
6913
7205
  }
6914
7206
  }
6915
7207
  async function projectsRemoveCommand(options) {
6916
7208
  try {
6917
- const id = options.id ?? fail3("projects remove requires --id");
7209
+ const id = options.id ?? fail("projects remove requires --id");
6918
7210
  if (!options.yes) {
6919
- fail3("projects remove requires --yes");
7211
+ fail("projects remove requires --yes");
6920
7212
  }
6921
7213
  await run3("remove", { id, yes: true }, options);
6922
7214
  } catch (error) {
6923
- exitWithError4(error);
7215
+ exitCommandError(error, options);
6924
7216
  }
6925
7217
  }
6926
7218
 
@@ -6928,11 +7220,6 @@ async function projectsRemoveCommand(options) {
6928
7220
  init_esm_shims();
6929
7221
  init_api();
6930
7222
  init_tui();
6931
- import process14 from "process";
6932
- var writeJson6 = (value) => {
6933
- process14.stdout.write(`${JSON.stringify(value, null, 2)}
6934
- `);
6935
- };
6936
7223
  var parseCsv3 = (input) => {
6937
7224
  if (!input) {
6938
7225
  return void 0;
@@ -6940,14 +7227,6 @@ var parseCsv3 = (input) => {
6940
7227
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
6941
7228
  return values.length > 0 ? values : void 0;
6942
7229
  };
6943
- var exitWithError5 = (error) => {
6944
- const message = error instanceof Error ? error.message : String(error);
6945
- log.error(message);
6946
- process14.exit(1);
6947
- };
6948
- var fail4 = (message) => {
6949
- throw new Error(message);
6950
- };
6951
7230
  var run4 = async (command, args, options) => {
6952
7231
  const apiOptions = {
6953
7232
  ...options.server ? { serverUrl: options.server } : {},
@@ -6962,69 +7241,69 @@ var run4 = async (command, args, options) => {
6962
7241
  apiOptions
6963
7242
  );
6964
7243
  if (options.json) {
6965
- writeJson6(result);
7244
+ writeJson(result);
6966
7245
  return;
6967
7246
  }
6968
7247
  log.success(`references ${command} completed`);
6969
7248
  };
6970
7249
  async function referencesListCommand(options) {
6971
7250
  try {
6972
- const ruleId = options.ruleId ?? fail4("references list requires --rule-id");
7251
+ const ruleId = options.ruleId ?? fail("references list requires --rule-id");
6973
7252
  await run4("list", { ruleId }, options);
6974
7253
  } catch (error) {
6975
- exitWithError5(error);
7254
+ exitCommandError(error, options);
6976
7255
  }
6977
7256
  }
6978
7257
  async function referencesGetCommand(options) {
6979
7258
  try {
6980
- const id = options.id ?? fail4("references get requires --id");
7259
+ const id = options.id ?? fail("references get requires --id");
6981
7260
  await run4("get", { id }, options);
6982
7261
  } catch (error) {
6983
- exitWithError5(error);
7262
+ exitCommandError(error, options);
6984
7263
  }
6985
7264
  }
6986
7265
  async function referencesCreateCommand(options) {
6987
7266
  try {
6988
- const ruleId = options.ruleId ?? fail4("references create requires --rule-id");
6989
- 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");
6990
7269
  await run4("create", { ruleId, file }, options);
6991
7270
  } catch (error) {
6992
- exitWithError5(error);
7271
+ exitCommandError(error, options);
6993
7272
  }
6994
7273
  }
6995
7274
  async function referencesUpdateCommand(options) {
6996
7275
  try {
6997
- const id = options.id ?? fail4("references update requires --id");
7276
+ const id = options.id ?? fail("references update requires --id");
6998
7277
  await run4(
6999
7278
  "update",
7000
7279
  { id, label: options.label, replaceFile: options.replaceFile },
7001
7280
  options
7002
7281
  );
7003
7282
  } catch (error) {
7004
- exitWithError5(error);
7283
+ exitCommandError(error, options);
7005
7284
  }
7006
7285
  }
7007
7286
  async function referencesRemoveCommand(options) {
7008
7287
  try {
7009
- const id = options.id ?? fail4("references remove requires --id");
7288
+ const id = options.id ?? fail("references remove requires --id");
7010
7289
  if (!options.yes) {
7011
- fail4("references remove requires --yes");
7290
+ fail("references remove requires --yes");
7012
7291
  }
7013
7292
  await run4("remove", { id, yes: true }, options);
7014
7293
  } catch (error) {
7015
- exitWithError5(error);
7294
+ exitCommandError(error, options);
7016
7295
  }
7017
7296
  }
7018
7297
  async function referencesReorderCommand(options) {
7019
7298
  try {
7020
- const ruleId = options.ruleId ?? fail4("references reorder requires --rule-id");
7299
+ const ruleId = options.ruleId ?? fail("references reorder requires --rule-id");
7021
7300
  const orderedIds = parseCsv3(options.orderedIds);
7022
7301
  if (!orderedIds || orderedIds.length === 0) {
7023
- fail4("references reorder requires --ordered-ids");
7302
+ fail("references reorder requires --ordered-ids");
7024
7303
  }
7025
7304
  await run4("reorder", { ruleId, orderedIds }, options);
7026
7305
  } catch (error) {
7027
- exitWithError5(error);
7306
+ exitCommandError(error, options);
7028
7307
  }
7029
7308
  }
7030
7309
 
@@ -7032,7 +7311,7 @@ async function referencesReorderCommand(options) {
7032
7311
  init_esm_shims();
7033
7312
  import { rm as rm7 } from "fs/promises";
7034
7313
  import { join as join14, resolve as resolve8 } from "path";
7035
- import process15 from "process";
7314
+ import process13 from "process";
7036
7315
  init_tui();
7037
7316
  async function collectInstalledSkills(detectedAgents, options) {
7038
7317
  const skillsToRemove = [];
@@ -7065,7 +7344,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7065
7344
  if (selected.length === 0) {
7066
7345
  log.error(`Skill '${options.skill}' not found.`);
7067
7346
  log.info("Run 'braid list' to see installed skills.");
7068
- process15.exit(1);
7347
+ process13.exit(1);
7069
7348
  }
7070
7349
  return selected;
7071
7350
  }
@@ -7084,7 +7363,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7084
7363
  });
7085
7364
  if (isCancel(result)) {
7086
7365
  cancel("Remove cancelled.");
7087
- process15.exit(0);
7366
+ process13.exit(0);
7088
7367
  }
7089
7368
  return result;
7090
7369
  }
@@ -7098,7 +7377,7 @@ async function confirmRemoval(selectedCount, options) {
7098
7377
  });
7099
7378
  if (isCancel(confirmed) || !confirmed) {
7100
7379
  cancel("Remove cancelled.");
7101
- process15.exit(0);
7380
+ process13.exit(0);
7102
7381
  }
7103
7382
  }
7104
7383
  async function removeSkill(skill, removeSpinner) {
@@ -7162,7 +7441,7 @@ async function removeCommand(options) {
7162
7441
  removeSpinner.stop("Remove failed");
7163
7442
  const message = error instanceof Error ? error.message : String(error);
7164
7443
  log.error(message);
7165
- process15.exit(1);
7444
+ process13.exit(1);
7166
7445
  }
7167
7446
  }
7168
7447
 
@@ -7221,6 +7500,7 @@ async function retractCommand(source, _options) {
7221
7500
  // src/commands/rollback.ts
7222
7501
  init_esm_shims();
7223
7502
  init_api();
7503
+ init_config();
7224
7504
  init_lockfile();
7225
7505
  init_tui();
7226
7506
  var PUBLIC_SOURCE_REGEX2 = /^@([a-z0-9-]+)\/([a-z0-9-]+)$/;
@@ -7253,6 +7533,8 @@ async function findPreviousActiveVersion(args) {
7253
7533
  return null;
7254
7534
  }
7255
7535
  async function rollbackCommand(source, options) {
7536
+ const config = await loadMergedConfigAsync();
7537
+ const serverUrl = options.server ?? config.serverUrl;
7256
7538
  intro(`Rolling back ${source}`);
7257
7539
  const rollbackSpinner = spinner();
7258
7540
  rollbackSpinner.start("Reading lockfile...");
@@ -7317,7 +7599,7 @@ async function rollbackCommand(source, options) {
7317
7599
  slug: publicSource.slug,
7318
7600
  currentVersion: parsed.version,
7319
7601
  ruleIds: currentValue.ruleIds,
7320
- server: options.server
7602
+ server: serverUrl
7321
7603
  });
7322
7604
  if (!result) {
7323
7605
  rollbackSpinner.stop("No active version found");
@@ -7360,7 +7642,6 @@ async function rollbackCommand(source, options) {
7360
7642
  init_esm_shims();
7361
7643
  init_api();
7362
7644
  init_tui();
7363
- import process16 from "process";
7364
7645
  var parseCsv4 = (input) => {
7365
7646
  if (!input) {
7366
7647
  return void 0;
@@ -7368,18 +7649,6 @@ var parseCsv4 = (input) => {
7368
7649
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
7369
7650
  return values.length > 0 ? values : void 0;
7370
7651
  };
7371
- var writeJson7 = (value) => {
7372
- process16.stdout.write(`${JSON.stringify(value, null, 2)}
7373
- `);
7374
- };
7375
- var exitWithError6 = (error) => {
7376
- const message = error instanceof Error ? error.message : String(error);
7377
- log.error(message);
7378
- process16.exit(1);
7379
- };
7380
- var fail5 = (message) => {
7381
- throw new Error(message);
7382
- };
7383
7652
  var run5 = async (command, args, options) => {
7384
7653
  const apiOptions = {
7385
7654
  ...options.server ? { serverUrl: options.server } : {},
@@ -7394,23 +7663,23 @@ var run5 = async (command, args, options) => {
7394
7663
  apiOptions
7395
7664
  );
7396
7665
  if (options.json) {
7397
- writeJson7(result);
7666
+ writeJson(result);
7398
7667
  return;
7399
7668
  }
7400
7669
  log.success(`rules ${command} completed`);
7401
7670
  };
7402
7671
  async function rulesGetCommand(options) {
7403
7672
  try {
7404
- const id = options.id ?? fail5("rules get requires --id");
7673
+ const id = options.id ?? fail("rules get requires --id");
7405
7674
  await run5("get", { id }, options);
7406
7675
  } catch (error) {
7407
- exitWithError6(error);
7676
+ exitCommandError(error, options);
7408
7677
  }
7409
7678
  }
7410
7679
  async function rulesCreateCommand(options) {
7411
7680
  try {
7412
- const title = options.title ?? fail5("rules create requires --title");
7413
- 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");
7414
7683
  await run5(
7415
7684
  "create",
7416
7685
  {
@@ -7423,12 +7692,12 @@ async function rulesCreateCommand(options) {
7423
7692
  options
7424
7693
  );
7425
7694
  } catch (error) {
7426
- exitWithError6(error);
7695
+ exitCommandError(error, options);
7427
7696
  }
7428
7697
  }
7429
7698
  async function rulesUpdateCommand(options) {
7430
7699
  try {
7431
- const id = options.id ?? fail5("rules update requires --id");
7700
+ const id = options.id ?? fail("rules update requires --id");
7432
7701
  await run5(
7433
7702
  "update",
7434
7703
  {
@@ -7441,120 +7710,120 @@ async function rulesUpdateCommand(options) {
7441
7710
  options
7442
7711
  );
7443
7712
  } catch (error) {
7444
- exitWithError6(error);
7713
+ exitCommandError(error, options);
7445
7714
  }
7446
7715
  }
7447
7716
  async function rulesRemoveCommand(options) {
7448
7717
  try {
7449
- const id = options.id ?? fail5("rules remove requires --id");
7718
+ const id = options.id ?? fail("rules remove requires --id");
7450
7719
  if (!options.yes) {
7451
- fail5("rules remove requires --yes");
7720
+ fail("rules remove requires --yes");
7452
7721
  }
7453
7722
  await run5("remove", { id, yes: true }, options);
7454
7723
  } catch (error) {
7455
- exitWithError6(error);
7724
+ exitCommandError(error, options);
7456
7725
  }
7457
7726
  }
7458
7727
  async function rulesEnableCommand(options) {
7459
7728
  try {
7460
- const id = options.id ?? fail5("rules enable requires --id");
7729
+ const id = options.id ?? fail("rules enable requires --id");
7461
7730
  await run5("enable", { id }, options);
7462
7731
  } catch (error) {
7463
- exitWithError6(error);
7732
+ exitCommandError(error, options);
7464
7733
  }
7465
7734
  }
7466
7735
  async function rulesDisableCommand(options) {
7467
7736
  try {
7468
- const id = options.id ?? fail5("rules disable requires --id");
7737
+ const id = options.id ?? fail("rules disable requires --id");
7469
7738
  await run5("disable", { id }, options);
7470
7739
  } catch (error) {
7471
- exitWithError6(error);
7740
+ exitCommandError(error, options);
7472
7741
  }
7473
7742
  }
7474
7743
  async function rulesMoveCommand(options) {
7475
7744
  try {
7476
- const id = options.id ?? fail5("rules move requires --id");
7477
- 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");
7478
7747
  await run5("move", { id, projectId }, options);
7479
7748
  } catch (error) {
7480
- exitWithError6(error);
7749
+ exitCommandError(error, options);
7481
7750
  }
7482
7751
  }
7483
7752
  async function rulesDuplicateCommand(options) {
7484
7753
  try {
7485
- const id = options.id ?? fail5("rules duplicate requires --id");
7754
+ const id = options.id ?? fail("rules duplicate requires --id");
7486
7755
  await run5(
7487
7756
  "duplicate",
7488
7757
  { id, targetProjectId: options.targetProjectId },
7489
7758
  options
7490
7759
  );
7491
7760
  } catch (error) {
7492
- exitWithError6(error);
7761
+ exitCommandError(error, options);
7493
7762
  }
7494
7763
  }
7495
7764
  async function rulesForkCommand(options) {
7496
7765
  try {
7497
- const id = options.id ?? fail5("rules fork requires --id");
7766
+ const id = options.id ?? fail("rules fork requires --id");
7498
7767
  await run5(
7499
7768
  "fork",
7500
7769
  { id, targetProjectId: options.targetProjectId },
7501
7770
  options
7502
7771
  );
7503
7772
  } catch (error) {
7504
- exitWithError6(error);
7773
+ exitCommandError(error, options);
7505
7774
  }
7506
7775
  }
7507
7776
  async function rulesSyncStatusCommand(options) {
7508
7777
  try {
7509
7778
  await run5("sync-status", { id: options.id }, options);
7510
7779
  } catch (error) {
7511
- exitWithError6(error);
7780
+ exitCommandError(error, options);
7512
7781
  }
7513
7782
  }
7514
7783
  async function rulesSyncHistoryCommand(options) {
7515
7784
  try {
7516
- const id = options.id ?? fail5("rules sync-history requires --id");
7785
+ const id = options.id ?? fail("rules sync-history requires --id");
7517
7786
  await run5("sync-history", { id }, options);
7518
7787
  } catch (error) {
7519
- exitWithError6(error);
7788
+ exitCommandError(error, options);
7520
7789
  }
7521
7790
  }
7522
7791
  async function rulesSyncEnableCommand(options) {
7523
7792
  try {
7524
- const id = options.id ?? fail5("rules sync-enable requires --id");
7793
+ const id = options.id ?? fail("rules sync-enable requires --id");
7525
7794
  await run5("sync-enable", { id }, options);
7526
7795
  } catch (error) {
7527
- exitWithError6(error);
7796
+ exitCommandError(error, options);
7528
7797
  }
7529
7798
  }
7530
7799
  async function rulesSyncDisableCommand(options) {
7531
7800
  try {
7532
- const id = options.id ?? fail5("rules sync-disable requires --id");
7801
+ const id = options.id ?? fail("rules sync-disable requires --id");
7533
7802
  await run5("sync-disable", { id }, options);
7534
7803
  } catch (error) {
7535
- exitWithError6(error);
7804
+ exitCommandError(error, options);
7536
7805
  }
7537
7806
  }
7538
7807
  async function rulesSyncCheckCommand(options) {
7539
7808
  try {
7540
- const id = options.id ?? fail5("rules sync-check requires --id");
7809
+ const id = options.id ?? fail("rules sync-check requires --id");
7541
7810
  await run5("sync-check", { id }, options);
7542
7811
  } catch (error) {
7543
- exitWithError6(error);
7812
+ exitCommandError(error, options);
7544
7813
  }
7545
7814
  }
7546
7815
  async function rulesSyncNowCommand(options) {
7547
7816
  try {
7548
- const id = options.id ?? fail5("rules sync-now requires --id");
7817
+ const id = options.id ?? fail("rules sync-now requires --id");
7549
7818
  await run5("sync-now", { id }, options);
7550
7819
  } catch (error) {
7551
- exitWithError6(error);
7820
+ exitCommandError(error, options);
7552
7821
  }
7553
7822
  }
7554
7823
 
7555
7824
  // src/commands/scaffold.ts
7556
7825
  init_esm_shims();
7557
- import process17 from "process";
7826
+ import process14 from "process";
7558
7827
 
7559
7828
  // src/lib/scaffold.ts
7560
7829
  init_esm_shims();
@@ -8514,11 +8783,11 @@ var normalizeReferenceList = (referenceLabel, availableNamesLabel, values, avail
8514
8783
  };
8515
8784
  var exitCancelled2 = () => {
8516
8785
  cancel("Scaffold cancelled.");
8517
- process17.exit(0);
8786
+ process14.exit(0);
8518
8787
  };
8519
- var exitWithError7 = (message) => {
8788
+ var exitWithError = (message) => {
8520
8789
  log.error(message);
8521
- process17.exit(1);
8790
+ process14.exit(1);
8522
8791
  };
8523
8792
  var requirePromptValue = (value) => {
8524
8793
  if (isCancel(value)) {
@@ -8531,7 +8800,7 @@ var resolveType = async (options) => {
8531
8800
  return options.type;
8532
8801
  }
8533
8802
  if (options.yes) {
8534
- return exitWithError7("--type is required when using --yes");
8803
+ return exitWithError("--type is required when using --yes");
8535
8804
  }
8536
8805
  const selected = requirePromptValue(
8537
8806
  await select({
@@ -8561,7 +8830,7 @@ var resolveName = async (options) => {
8561
8830
  return options.name.trim();
8562
8831
  }
8563
8832
  if (options.yes) {
8564
- return exitWithError7("--name is required when using --yes");
8833
+ return exitWithError("--name is required when using --yes");
8565
8834
  }
8566
8835
  const value = requirePromptValue(
8567
8836
  await text({
@@ -8647,7 +8916,7 @@ var resolveOptionalReference = async (label, flagValue, options, availableNames)
8647
8916
  return selected || void 0;
8648
8917
  };
8649
8918
  var buildScaffoldInput = async (options) => {
8650
- const context = await inspectScaffoldDirectory(process17.cwd());
8919
+ const context = await inspectScaffoldDirectory(process14.cwd());
8651
8920
  const availableSkillNames = context.manifest.skills.map(
8652
8921
  (entry) => entry.name
8653
8922
  );
@@ -8701,7 +8970,7 @@ var buildScaffoldInput = async (options) => {
8701
8970
  availableWorkflowNames
8702
8971
  ) : void 0;
8703
8972
  return {
8704
- cwd: process17.cwd(),
8973
+ cwd: process14.cwd(),
8705
8974
  type,
8706
8975
  name,
8707
8976
  title,
@@ -8730,10 +8999,10 @@ var finishScaffoldSuccess = (type, name, createdFiles) => {
8730
8999
  var isMockedProcessExitError = (error) => error instanceof Error && error.message.startsWith("process.exit:");
8731
9000
  var handleScaffoldCommandFailure = (error) => {
8732
9001
  if (error instanceof ScaffoldError) {
8733
- exitWithError7(error.message);
9002
+ exitWithError(error.message);
8734
9003
  }
8735
9004
  if (error instanceof Error && !isMockedProcessExitError(error)) {
8736
- exitWithError7(error.message);
9005
+ exitWithError(error.message);
8737
9006
  }
8738
9007
  throw error;
8739
9008
  };