@braid-cloud/cli 0.1.18 → 0.1.20

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,296 @@ 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, resolvePublicRequestServerUrl, 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
+ resolvePublicRequestServerUrl = (serverUrl, apiKey) => resolveApiRequestServerUrl(serverUrl, apiKey);
993
+ normalizeApiServerBaseUrl = (serverUrl) => {
994
+ const trimmed = serverUrl.replace(TRAILING_SLASH_REGEX, "");
995
+ try {
996
+ const url = new URL(trimmed);
997
+ return `${url.origin}${url.pathname === "/" ? "" : url.pathname}`;
998
+ } catch {
999
+ return trimmed;
1000
+ }
1001
+ };
1002
+ readJsonResponseAsync = async (response) => {
1003
+ const raw = await response.text();
1004
+ if (raw.trim().length === 0) {
1005
+ if (response.ok) {
1006
+ throw new Error("Empty API response");
1007
+ }
1008
+ return null;
1009
+ }
1010
+ try {
1011
+ return JSON.parse(raw);
1012
+ } catch (error) {
1013
+ if (!response.ok) {
1014
+ return null;
1015
+ }
1016
+ throw error;
1017
+ }
1018
+ };
1019
+ readJsonResponse = (response) => Effect4.tryPromise({
1020
+ try: () => readJsonResponseAsync(response),
1021
+ catch: () => new NetworkError({ message: "Failed to parse API response" })
1022
+ });
1023
+ getApiErrorResponse = (json) => {
1024
+ if (typeof json !== "object" || json === null || Array.isArray(json)) {
1025
+ return {};
1026
+ }
1027
+ return json;
1028
+ };
1029
+ isAuthenticationErrorResponse = (response, json) => {
1030
+ if (response.status === 401) {
1031
+ return true;
1032
+ }
1033
+ const errorResponse = getApiErrorResponse(json);
1034
+ return RAW_AUTH_ERROR_PATTERN.test(errorResponse.error ?? "");
1035
+ };
591
1036
  parseResponse = (response, json) => {
592
1037
  if (!response.ok) {
593
- const errorResponse = json;
594
- if (response.status === 401) {
1038
+ const errorResponse = getApiErrorResponse(json);
1039
+ if (isAuthenticationErrorResponse(response, json)) {
595
1040
  return Effect4.fail(
596
1041
  new AuthenticationError({
597
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1042
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
598
1043
  })
599
1044
  );
600
1045
  }
@@ -653,10 +1098,10 @@ var init_api = __esm({
653
1098
  Effect4.tryPromise({
654
1099
  try: () => fetch(url.toString(), {
655
1100
  method: "GET",
656
- headers: {
1101
+ headers: mergeAutomationHeaders({
657
1102
  "Content-Type": "application/json",
658
1103
  ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
659
- }
1104
+ })
660
1105
  }),
661
1106
  catch: (e) => new NetworkError({
662
1107
  message: `Failed to connect to ${serverUrl}`,
@@ -666,7 +1111,7 @@ var init_api = __esm({
666
1111
  Effect4.flatMap(
667
1112
  (response) => pipe4(
668
1113
  Effect4.tryPromise({
669
- try: () => response.json(),
1114
+ try: () => readJsonResponseAsync(response),
670
1115
  catch: () => new NetworkError({ message: "Failed to parse API response" })
671
1116
  }),
672
1117
  Effect4.flatMap((json) => parseResponse(response, json))
@@ -675,11 +1120,11 @@ var init_api = __esm({
675
1120
  );
676
1121
  parseScopeOptionsResponse = (response, json) => {
677
1122
  if (!response.ok) {
678
- const errorResponse = json;
679
- if (response.status === 401) {
1123
+ const errorResponse = getApiErrorResponse(json);
1124
+ if (isAuthenticationErrorResponse(response, json)) {
680
1125
  return Effect4.fail(
681
1126
  new AuthenticationError({
682
- message: errorResponse.error || "Invalid or expired API key. Run 'braid auth' to re-authenticate."
1127
+ message: normalizeAuthenticationErrorMessage(errorResponse.error)
683
1128
  })
684
1129
  );
685
1130
  }
@@ -698,33 +1143,37 @@ var init_api = __esm({
698
1143
  apiKey: resolveApiKey(options.apiKey),
699
1144
  serverUrl: resolveServerUrl(options.serverUrl)
700
1145
  }),
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(
1146
+ Effect4.flatMap(
1147
+ ({ apiKey, serverUrl }) => pipe4(
1148
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1149
+ Effect4.flatMap((apiServerUrl) => {
1150
+ const url = buildApiUrl(apiServerUrl, "/api/skills/scope-options");
1151
+ return pipe4(
719
1152
  Effect4.tryPromise({
720
- try: () => response.json(),
721
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1153
+ try: () => fetch(url.toString(), {
1154
+ method: "GET",
1155
+ headers: mergeAutomationHeaders({
1156
+ Authorization: `Bearer ${apiKey}`,
1157
+ "Content-Type": "application/json"
1158
+ })
1159
+ }),
1160
+ catch: (e) => new NetworkError({
1161
+ message: `Failed to connect to ${apiServerUrl}`,
1162
+ cause: e
1163
+ })
722
1164
  }),
723
- Effect4.flatMap((json) => parseScopeOptionsResponse(response, json))
724
- )
725
- )
726
- );
727
- })
1165
+ Effect4.flatMap(
1166
+ (response) => pipe4(
1167
+ readJsonResponse(response),
1168
+ Effect4.flatMap(
1169
+ (json) => parseScopeOptionsResponse(response, json)
1170
+ )
1171
+ )
1172
+ )
1173
+ );
1174
+ })
1175
+ )
1176
+ )
728
1177
  );
729
1178
  buildLibraryOptionsUrl = (serverUrl, options) => {
730
1179
  const url = buildApiUrl(serverUrl, "/api/skills/library-options");
@@ -758,41 +1207,57 @@ var init_api = __esm({
758
1207
  }),
759
1208
  Effect4.flatMap(
760
1209
  ({ 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
- }),
1210
+ resolveApiRequestServerUrl(serverUrl, apiKey),
774
1211
  Effect4.flatMap(
775
- (response) => pipe4(
1212
+ (apiServerUrl) => pipe4(
776
1213
  Effect4.tryPromise({
777
- try: () => response.json(),
778
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1214
+ try: () => fetch(
1215
+ buildLibraryOptionsUrl(apiServerUrl, options).toString(),
1216
+ {
1217
+ method: "GET",
1218
+ headers: mergeAutomationHeaders({
1219
+ Authorization: `Bearer ${apiKey}`,
1220
+ "Content-Type": "application/json"
1221
+ })
1222
+ }
1223
+ ),
1224
+ catch: (e) => new NetworkError({
1225
+ message: `Failed to connect to ${apiServerUrl}`,
1226
+ cause: e
1227
+ })
779
1228
  }),
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
- })
1229
+ Effect4.flatMap(
1230
+ (response) => pipe4(
1231
+ readJsonResponse(response),
1232
+ Effect4.flatMap(
1233
+ (json) => {
1234
+ if (!response.ok) {
1235
+ return pipe4(
1236
+ parseResponse(response, json),
1237
+ Effect4.flatMap(
1238
+ () => Effect4.fail(
1239
+ new ApiError({
1240
+ message: "API request failed",
1241
+ code: "UNKNOWN_ERROR",
1242
+ status: response.status
1243
+ })
1244
+ )
1245
+ ),
1246
+ Effect4.catchTag(
1247
+ "AuthenticationError",
1248
+ (error) => Effect4.fail(error)
1249
+ ),
1250
+ Effect4.catchTag(
1251
+ "ApiError",
1252
+ (error) => Effect4.fail(error)
1253
+ )
1254
+ );
1255
+ }
1256
+ return Effect4.succeed(json);
1257
+ }
1258
+ )
1259
+ )
1260
+ )
796
1261
  )
797
1262
  )
798
1263
  )
@@ -803,10 +1268,15 @@ var init_api = __esm({
803
1268
  apiKey: options.demo ? Effect4.succeed(options.apiKey) : resolveApiKey(options.apiKey),
804
1269
  serverUrl: resolveServerUrl(options.serverUrl)
805
1270
  }),
806
- Effect4.flatMap(({ apiKey, serverUrl }) => {
807
- const url = buildExportUrl(serverUrl, options);
808
- return executeApiRequest(url, apiKey, serverUrl);
809
- })
1271
+ Effect4.flatMap(
1272
+ ({ apiKey, serverUrl }) => pipe4(
1273
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1274
+ Effect4.flatMap((apiServerUrl) => {
1275
+ const url = buildExportUrl(apiServerUrl, options);
1276
+ return executeApiRequest(url, apiKey, apiServerUrl);
1277
+ })
1278
+ )
1279
+ )
810
1280
  );
811
1281
  runLifecycleCommand = (request, options = {}) => pipe4(
812
1282
  Effect4.all({
@@ -814,45 +1284,56 @@ var init_api = __esm({
814
1284
  serverUrl: resolveServerUrl(options.serverUrl)
815
1285
  }),
816
1286
  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."
1287
+ ({ apiKey, serverUrl }) => pipe4(
1288
+ resolveApiRequestServerUrl(serverUrl, apiKey),
1289
+ Effect4.flatMap(
1290
+ (apiServerUrl) => Effect4.tryPromise({
1291
+ try: async () => {
1292
+ const response = await fetch(
1293
+ buildApiUrl(apiServerUrl, "/api/lifecycle").toString(),
1294
+ {
1295
+ method: "POST",
1296
+ headers: mergeAutomationHeaders({
1297
+ "Content-Type": "application/json",
1298
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
1299
+ }),
1300
+ body: JSON.stringify({ ...request, demo: options.demo })
1301
+ }
1302
+ );
1303
+ const json = await readJsonResponseAsync(response).catch(() => {
1304
+ throw new NetworkError({
1305
+ message: "Failed to parse API response"
1306
+ });
1307
+ });
1308
+ if (!response.ok) {
1309
+ const errorResponse = getApiErrorResponse(json);
1310
+ if (isAuthenticationErrorResponse(response, json)) {
1311
+ throw new AuthenticationError({
1312
+ message: normalizeAuthenticationErrorMessage(
1313
+ errorResponse.error
1314
+ )
1315
+ });
1316
+ }
1317
+ throw new ApiError({
1318
+ message: errorResponse.error || "API request failed",
1319
+ code: errorResponse.code || "UNKNOWN_ERROR",
1320
+ status: response.status
1321
+ });
1322
+ }
1323
+ return json;
1324
+ },
1325
+ catch: (error) => {
1326
+ if (error instanceof ApiError || error instanceof AuthenticationError || error instanceof NetworkError) {
1327
+ return error;
1328
+ }
1329
+ return new NetworkError({
1330
+ message: `Failed to connect to ${apiServerUrl}`,
1331
+ cause: error
836
1332
  });
837
1333
  }
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
- })
1334
+ })
1335
+ )
1336
+ )
856
1337
  )
857
1338
  );
858
1339
  DEFAULT_PUBLIC_SERVER_URL = "https://braid.cloud";
@@ -879,7 +1360,7 @@ var init_api = __esm({
879
1360
  })
880
1361
  );
881
1362
  }
882
- const errorResponse = json;
1363
+ const errorResponse = getApiErrorResponse(json);
883
1364
  return Effect4.fail(
884
1365
  new ApiError({
885
1366
  message: errorResponse.error || "API request failed",
@@ -888,72 +1369,84 @@ var init_api = __esm({
888
1369
  })
889
1370
  );
890
1371
  };
891
- fetchPublicMetadata = (handle, slug, serverUrl, version) => {
892
- const baseUrl = (serverUrl ?? DEFAULT_PUBLIC_SERVER_URL).replace(
1372
+ fetchPublicMetadata = (handle, slug, serverUrl, version, apiKey) => {
1373
+ const requestedBaseUrl = (serverUrl ?? DEFAULT_PUBLIC_SERVER_URL).replace(
893
1374
  TRAILING_SLASH_REGEX,
894
1375
  ""
895
1376
  );
896
- const url = new URL(`${baseUrl}/api/public/@${handle}/${slug}`);
897
- if (version !== void 0) {
898
- url.searchParams.set("version", String(version));
899
- }
900
1377
  return pipe4(
901
- Effect4.tryPromise({
902
- try: () => fetch(url.toString(), { method: "GET" }),
903
- catch: (e) => new NetworkError({
904
- message: `Failed to connect to ${baseUrl}`,
905
- cause: e
906
- })
907
- }),
908
- Effect4.flatMap(
909
- (response) => pipe4(
1378
+ resolvePublicRequestServerUrl(requestedBaseUrl, apiKey),
1379
+ Effect4.flatMap((baseUrl) => {
1380
+ const url = new URL(`${baseUrl}/api/public/@${handle}/${slug}`);
1381
+ if (version !== void 0) {
1382
+ url.searchParams.set("version", String(version));
1383
+ }
1384
+ return pipe4(
910
1385
  Effect4.tryPromise({
911
- try: () => response.json(),
912
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1386
+ try: () => fetch(url.toString(), {
1387
+ method: "GET",
1388
+ headers: mergeAutomationHeaders()
1389
+ }),
1390
+ catch: (e) => new NetworkError({
1391
+ message: `Failed to connect to ${baseUrl}`,
1392
+ cause: e
1393
+ })
913
1394
  }),
914
1395
  Effect4.flatMap(
915
- (json) => handlePublicApiResponse(response, json)
1396
+ (response) => pipe4(
1397
+ readJsonResponse(response),
1398
+ Effect4.flatMap(
1399
+ (json) => handlePublicApiResponse(response, json)
1400
+ )
1401
+ )
916
1402
  )
917
- )
918
- )
1403
+ );
1404
+ })
919
1405
  );
920
1406
  };
921
- fetchPublicExport = (handle, slug, ruleIds, serverUrl, version) => {
922
- const baseUrl = (serverUrl ?? DEFAULT_PUBLIC_SERVER_URL).replace(
1407
+ fetchPublicExport = (handle, slug, ruleIds, serverUrl, version, apiKey) => {
1408
+ const requestedBaseUrl = (serverUrl ?? DEFAULT_PUBLIC_SERVER_URL).replace(
923
1409
  TRAILING_SLASH_REGEX,
924
1410
  ""
925
1411
  );
926
- const url = new URL(`${baseUrl}/api/public/@${handle}/${slug}/export`);
927
- if (ruleIds && ruleIds.length > 0) {
928
- url.searchParams.set("ruleIds", ruleIds.join(","));
929
- }
930
- if (version !== void 0) {
931
- url.searchParams.set("version", String(version));
932
- }
933
1412
  return pipe4(
934
- Effect4.tryPromise({
935
- try: () => fetch(url.toString(), { method: "GET" }),
936
- catch: (e) => new NetworkError({
937
- message: `Failed to connect to ${baseUrl}`,
938
- cause: e
939
- })
940
- }),
941
- Effect4.flatMap(
942
- (response) => pipe4(
1413
+ resolvePublicRequestServerUrl(requestedBaseUrl, apiKey),
1414
+ Effect4.flatMap((baseUrl) => {
1415
+ const url = new URL(`${baseUrl}/api/public/@${handle}/${slug}/export`);
1416
+ if (ruleIds && ruleIds.length > 0) {
1417
+ url.searchParams.set("ruleIds", ruleIds.join(","));
1418
+ }
1419
+ if (version !== void 0) {
1420
+ url.searchParams.set("version", String(version));
1421
+ }
1422
+ return pipe4(
943
1423
  Effect4.tryPromise({
944
- try: () => response.json(),
945
- catch: () => new NetworkError({ message: "Failed to parse API response" })
1424
+ try: () => fetch(url.toString(), {
1425
+ method: "GET",
1426
+ headers: mergeAutomationHeaders()
1427
+ }),
1428
+ catch: (e) => new NetworkError({
1429
+ message: `Failed to connect to ${baseUrl}`,
1430
+ cause: e
1431
+ })
946
1432
  }),
947
1433
  Effect4.flatMap(
948
- (json) => handlePublicApiResponse(response, json)
1434
+ (response) => pipe4(
1435
+ readJsonResponse(response),
1436
+ Effect4.flatMap(
1437
+ (json) => handlePublicApiResponse(response, json)
1438
+ )
1439
+ )
949
1440
  )
950
- )
951
- )
1441
+ );
1442
+ })
952
1443
  );
953
1444
  };
954
- fetchPublicMetadataAsync = (handle, slug, serverUrl, version) => Effect4.runPromise(fetchPublicMetadata(handle, slug, serverUrl, version));
955
- fetchPublicExportAsync = (handle, slug, ruleIds, serverUrl, version) => Effect4.runPromise(
956
- fetchPublicExport(handle, slug, ruleIds, serverUrl, version)
1445
+ fetchPublicMetadataAsync = (handle, slug, serverUrl, version, apiKey) => Effect4.runPromise(
1446
+ fetchPublicMetadata(handle, slug, serverUrl, version, apiKey)
1447
+ );
1448
+ fetchPublicExportAsync = (handle, slug, ruleIds, serverUrl, version, apiKey) => Effect4.runPromise(
1449
+ fetchPublicExport(handle, slug, ruleIds, serverUrl, version, apiKey)
957
1450
  );
958
1451
  validateApiKey = (apiKey, serverUrl) => pipe4(
959
1452
  Effect4.try({
@@ -975,10 +1468,10 @@ var init_api = __esm({
975
1468
  url.searchParams.set("profile", "default");
976
1469
  const response = await fetch(url.toString(), {
977
1470
  method: "GET",
978
- headers: {
1471
+ headers: mergeAutomationHeaders({
979
1472
  Authorization: `Bearer ${apiKey}`,
980
1473
  "Content-Type": "application/json"
981
- }
1474
+ })
982
1475
  });
983
1476
  return response.status !== 401;
984
1477
  },
@@ -998,7 +1491,7 @@ var init_api = __esm({
998
1491
  });
999
1492
 
1000
1493
  // src/lib/tui.ts
1001
- import process5 from "process";
1494
+ import process7 from "process";
1002
1495
  import {
1003
1496
  cancel as clackCancel,
1004
1497
  confirm as clackConfirm,
@@ -1023,19 +1516,19 @@ function spinner() {
1023
1516
  return {
1024
1517
  start: (message) => {
1025
1518
  if (message) {
1026
- process5.stdout.write(`${message}
1519
+ process7.stdout.write(`${message}
1027
1520
  `);
1028
1521
  }
1029
1522
  },
1030
1523
  stop: (message) => {
1031
1524
  if (message) {
1032
- process5.stdout.write(`${message}
1525
+ process7.stdout.write(`${message}
1033
1526
  `);
1034
1527
  }
1035
1528
  },
1036
1529
  message: (message) => {
1037
1530
  if (message) {
1038
- process5.stdout.write(`${message}
1531
+ process7.stdout.write(`${message}
1039
1532
  `);
1040
1533
  }
1041
1534
  }
@@ -1045,7 +1538,7 @@ function intro(message) {
1045
1538
  if (isTTY) {
1046
1539
  clackIntro(message);
1047
1540
  } else {
1048
- process5.stdout.write(`
1541
+ process7.stdout.write(`
1049
1542
  ${message}
1050
1543
  `);
1051
1544
  }
@@ -1054,7 +1547,7 @@ function outro(message) {
1054
1547
  if (isTTY) {
1055
1548
  clackOutro(message);
1056
1549
  } else {
1057
- process5.stdout.write(`${message}
1550
+ process7.stdout.write(`${message}
1058
1551
 
1059
1552
  `);
1060
1553
  }
@@ -1064,7 +1557,7 @@ var init_tui = __esm({
1064
1557
  "src/lib/tui.ts"() {
1065
1558
  "use strict";
1066
1559
  init_esm_shims();
1067
- isTTY = Boolean(process5.stdout.isTTY);
1560
+ isTTY = Boolean(process7.stdout.isTTY);
1068
1561
  cancel = clackCancel;
1069
1562
  confirm = clackConfirm;
1070
1563
  isCancel = clackIsCancel;
@@ -1174,10 +1667,10 @@ var scope_exports = {};
1174
1667
  __export(scope_exports, {
1175
1668
  scopeCommand: () => scopeCommand
1176
1669
  });
1177
- import process7 from "process";
1670
+ import process9 from "process";
1178
1671
  function exitCancelled(message) {
1179
1672
  cancel(message);
1180
- process7.exit(0);
1673
+ process9.exit(0);
1181
1674
  }
1182
1675
  async function selectTargetFile(options) {
1183
1676
  if (options.file) {
@@ -1529,7 +2022,7 @@ async function scopeCommand(options) {
1529
2022
  } catch (error) {
1530
2023
  loadSpinner.stop("Failed to load scope options");
1531
2024
  log.error(error instanceof Error ? error.message : String(error));
1532
- process7.exit(1);
2025
+ process9.exit(1);
1533
2026
  }
1534
2027
  }
1535
2028
  var init_scope = __esm({
@@ -1568,7 +2061,7 @@ __export(lockfile_exports, {
1568
2061
  });
1569
2062
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
1570
2063
  import { join as join6 } from "path";
1571
- import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2064
+ import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
1572
2065
  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
2066
  var init_lockfile = __esm({
1574
2067
  "src/lib/lockfile.ts"() {
@@ -1578,13 +2071,13 @@ var init_lockfile = __esm({
1578
2071
  LOCKFILE_VERSION = 1;
1579
2072
  MARKETPLACE_KEY_REGEX = /^@([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1580
2073
  ORG_KEY_REGEX = /^([a-z0-9-]+)\/([a-z0-9-]+)@(\d+)$/;
1581
- LockfileReadError = class extends Data4.TaggedError("LockfileReadError") {
2074
+ LockfileReadError = class extends Data5.TaggedError("LockfileReadError") {
1582
2075
  };
1583
- LockfileWriteError = class extends Data4.TaggedError("LockfileWriteError") {
2076
+ LockfileWriteError = class extends Data5.TaggedError("LockfileWriteError") {
1584
2077
  };
1585
- LockfileVersionError = class extends Data4.TaggedError("LockfileVersionError") {
2078
+ LockfileVersionError = class extends Data5.TaggedError("LockfileVersionError") {
1586
2079
  };
1587
- LockfileCorruptError = class extends Data4.TaggedError("LockfileCorruptError") {
2080
+ LockfileCorruptError = class extends Data5.TaggedError("LockfileCorruptError") {
1588
2081
  };
1589
2082
  getLockfilePath = (dir) => join6(dir, LOCKFILE_FILENAME);
1590
2083
  emptyLockfile = () => ({
@@ -1593,27 +2086,27 @@ var init_lockfile = __esm({
1593
2086
  });
1594
2087
  readLockfile = (dir) => {
1595
2088
  const lockfilePath = getLockfilePath(dir);
1596
- return pipe5(
1597
- Effect5.tryPromise({
2089
+ return pipe6(
2090
+ Effect6.tryPromise({
1598
2091
  try: () => readFile3(lockfilePath, "utf-8"),
1599
2092
  catch: () => new LockfileReadError({ path: lockfilePath, cause: "not found" })
1600
2093
  }),
1601
- Effect5.catchTag(
2094
+ Effect6.catchTag(
1602
2095
  "LockfileReadError",
1603
- () => Effect5.succeed(null)
2096
+ () => Effect6.succeed(null)
1604
2097
  ),
1605
- Effect5.flatMap((content) => {
2098
+ Effect6.flatMap((content) => {
1606
2099
  if (content === null) {
1607
- return Effect5.succeed(null);
2100
+ return Effect6.succeed(null);
1608
2101
  }
1609
- return pipe5(
1610
- Effect5.try({
2102
+ return pipe6(
2103
+ Effect6.try({
1611
2104
  try: () => JSON.parse(content),
1612
2105
  catch: (cause) => new LockfileCorruptError({ path: lockfilePath, cause })
1613
2106
  }),
1614
- Effect5.flatMap((parsed) => {
2107
+ Effect6.flatMap((parsed) => {
1615
2108
  if (parsed.lockfileVersion !== LOCKFILE_VERSION) {
1616
- return Effect5.fail(
2109
+ return Effect6.fail(
1617
2110
  new LockfileVersionError({
1618
2111
  path: lockfilePath,
1619
2112
  expected: LOCKFILE_VERSION,
@@ -1621,7 +2114,7 @@ var init_lockfile = __esm({
1621
2114
  })
1622
2115
  );
1623
2116
  }
1624
- return Effect5.succeed(parsed);
2117
+ return Effect6.succeed(parsed);
1625
2118
  })
1626
2119
  );
1627
2120
  })
@@ -1629,7 +2122,7 @@ var init_lockfile = __esm({
1629
2122
  };
1630
2123
  writeLockfile = (dir, lockfile) => {
1631
2124
  const lockfilePath = getLockfilePath(dir);
1632
- return Effect5.tryPromise({
2125
+ return Effect6.tryPromise({
1633
2126
  try: () => writeFile4(
1634
2127
  lockfilePath,
1635
2128
  `${JSON.stringify(lockfile, null, 2)}
@@ -1646,26 +2139,26 @@ var init_lockfile = __esm({
1646
2139
  }
1647
2140
  return entry;
1648
2141
  };
1649
- upsertLockfileEntry = (dir, key, entry) => pipe5(
2142
+ upsertLockfileEntry = (dir, key, entry) => pipe6(
1650
2143
  readLockfile(dir),
1651
- Effect5.map((existing) => existing ?? emptyLockfile()),
1652
- Effect5.map((lockfile) => ({
2144
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2145
+ Effect6.map((lockfile) => ({
1653
2146
  ...lockfile,
1654
2147
  resolved: {
1655
2148
  ...lockfile.resolved,
1656
2149
  [key]: entry
1657
2150
  }
1658
2151
  })),
1659
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2152
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1660
2153
  );
1661
- removeLockfileEntry = (dir, key) => pipe5(
2154
+ removeLockfileEntry = (dir, key) => pipe6(
1662
2155
  readLockfile(dir),
1663
- Effect5.map((existing) => existing ?? emptyLockfile()),
1664
- Effect5.map((lockfile) => {
2156
+ Effect6.map((existing) => existing ?? emptyLockfile()),
2157
+ Effect6.map((lockfile) => {
1665
2158
  const { [key]: _removed, ...rest } = lockfile.resolved;
1666
2159
  return { ...lockfile, resolved: rest };
1667
2160
  }),
1668
- Effect5.flatMap((lockfile) => writeLockfile(dir, lockfile))
2161
+ Effect6.flatMap((lockfile) => writeLockfile(dir, lockfile))
1669
2162
  );
1670
2163
  buildLockfileKey = (source, handle, slug, version) => {
1671
2164
  const prefix = source === "marketplace" ? `@${handle}` : handle;
@@ -1692,10 +2185,10 @@ var init_lockfile = __esm({
1692
2185
  }
1693
2186
  return null;
1694
2187
  };
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));
2188
+ readLockfileAsync = (dir) => Effect6.runPromise(readLockfile(dir));
2189
+ writeLockfileAsync = (dir, lockfile) => Effect6.runPromise(writeLockfile(dir, lockfile));
2190
+ upsertLockfileEntryAsync = (dir, key, entry) => Effect6.runPromise(upsertLockfileEntry(dir, key, entry));
2191
+ removeLockfileEntryAsync = (dir, key) => Effect6.runPromise(removeLockfileEntry(dir, key));
1699
2192
  }
1700
2193
  });
1701
2194
 
@@ -2271,15 +2764,262 @@ var resolveHookConfigPath = (agent, options) => {
2271
2764
  if (options.global) {
2272
2765
  return agent.hookGlobalConfigPath;
2273
2766
  }
2274
- if (!agent.hookProjectConfigPath) {
2275
- return void 0;
2767
+ if (!agent.hookProjectConfigPath) {
2768
+ return void 0;
2769
+ }
2770
+ const cwd = options.projectRoot ?? process2.cwd();
2771
+ return join(cwd, agent.hookProjectConfigPath);
2772
+ };
2773
+
2774
+ // src/commands/agents.ts
2775
+ init_api();
2776
+
2777
+ // src/lib/command-output.ts
2778
+ init_esm_shims();
2779
+ init_api();
2780
+ import process8 from "process";
2781
+
2782
+ // src/lib/marketplace-api.ts
2783
+ init_esm_shims();
2784
+ init_automation_headers();
2785
+ init_config();
2786
+ init_device_auth();
2787
+ import { Data as Data4, Effect as Effect5, pipe as pipe5 } from "effect";
2788
+ var TRAILING_SLASH_REGEX2 = /\/$/;
2789
+ var SESSION_TOKEN_PREFIX2 = "brs_";
2790
+ var MarketplaceApiError = class extends Data4.TaggedError("MarketplaceApiError") {
2791
+ };
2792
+ var isLocalHost2 = (hostname2) => hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1";
2793
+ var isConvexSiteHost2 = (hostname2) => hostname2.endsWith(".convex.site");
2794
+ var isSessionApiServerUrl2 = (serverUrl) => {
2795
+ try {
2796
+ const hostname2 = new URL(serverUrl).hostname;
2797
+ return isConvexSiteHost2(hostname2) || isLocalHost2(hostname2);
2798
+ } catch {
2799
+ return false;
2800
+ }
2801
+ };
2802
+ var resolveApiKey2 = (provided) => {
2803
+ if (provided) {
2804
+ return Effect5.succeed(provided);
2805
+ }
2806
+ return pipe5(
2807
+ Effect5.tryPromise({
2808
+ try: () => getApiKeyAsync(),
2809
+ catch: () => new MarketplaceApiError({
2810
+ message: "Failed to load API key",
2811
+ status: 500,
2812
+ code: "CONFIG_ERROR"
2813
+ })
2814
+ }),
2815
+ Effect5.flatMap(
2816
+ (token) => token ? Effect5.succeed(token) : Effect5.fail(
2817
+ new MarketplaceApiError({
2818
+ message: "Not signed in. Run 'braid auth'.",
2819
+ status: 401,
2820
+ code: "AUTH_REQUIRED"
2821
+ })
2822
+ )
2823
+ )
2824
+ );
2825
+ };
2826
+ var resolveServer = (provided) => {
2827
+ if (provided) {
2828
+ return Effect5.succeed(provided.replace(TRAILING_SLASH_REGEX2, ""));
2829
+ }
2830
+ return pipe5(
2831
+ Effect5.tryPromise({
2832
+ try: () => getServerUrlAsync(),
2833
+ catch: () => new MarketplaceApiError({
2834
+ message: "Failed to load server URL",
2835
+ status: 500,
2836
+ code: "CONFIG_ERROR"
2837
+ })
2838
+ }),
2839
+ Effect5.map((server) => server.replace(TRAILING_SLASH_REGEX2, ""))
2840
+ );
2841
+ };
2842
+ var resolveRequestServer = (server, apiKey) => {
2843
+ if (!apiKey.startsWith(SESSION_TOKEN_PREFIX2) || isSessionApiServerUrl2(server)) {
2844
+ return Effect5.succeed(server);
2845
+ }
2846
+ return Effect5.tryPromise({
2847
+ try: async () => {
2848
+ const authConfig = await fetchAuthConfig(server);
2849
+ return authConfig.convexSiteUrl.replace(TRAILING_SLASH_REGEX2, "");
2850
+ },
2851
+ catch: () => new MarketplaceApiError({
2852
+ message: `Failed to resolve marketplace API server for ${server}`,
2853
+ status: 500,
2854
+ code: "CONFIG_ERROR"
2855
+ })
2856
+ });
2857
+ };
2858
+ var requestJson = (url, options, requestOptions = {}) => pipe5(
2859
+ Effect5.tryPromise({
2860
+ try: () => fetch(url, options),
2861
+ catch: () => new MarketplaceApiError({
2862
+ message: "Network error",
2863
+ status: 503,
2864
+ code: "NETWORK_ERROR"
2865
+ })
2866
+ }),
2867
+ Effect5.flatMap(
2868
+ (response) => Effect5.tryPromise({
2869
+ try: async () => {
2870
+ const payload = await response.json().catch(() => null);
2871
+ if (!response.ok) {
2872
+ const normalizedMessage = requestOptions.normalizeErrorMessage?.({
2873
+ payload,
2874
+ response,
2875
+ url
2876
+ });
2877
+ throw new MarketplaceApiError({
2878
+ message: normalizedMessage ?? (response.status === 404 ? requestOptions.notFoundMessage : void 0) ?? payload?.error ?? payload?.message ?? `Request failed (HTTP ${response.status} from ${new URL(url).origin})`,
2879
+ status: response.status,
2880
+ code: payload?.code ?? "REQUEST_ERROR"
2881
+ });
2882
+ }
2883
+ return payload;
2884
+ },
2885
+ catch: (error) => error instanceof MarketplaceApiError ? error : new MarketplaceApiError({
2886
+ message: "Invalid response",
2887
+ status: 500,
2888
+ code: "PARSE_ERROR"
2889
+ })
2890
+ })
2891
+ )
2892
+ );
2893
+ var fetchMarketplaceLibrary = (options) => pipe5(
2894
+ Effect5.all({
2895
+ server: resolveServer(options.server),
2896
+ apiKey: resolveApiKey2(options.apiKey)
2897
+ }),
2898
+ Effect5.flatMap(
2899
+ ({ server, apiKey }) => Effect5.all({
2900
+ apiKey: Effect5.succeed(apiKey),
2901
+ server: resolveRequestServer(server, apiKey)
2902
+ })
2903
+ ),
2904
+ Effect5.flatMap(
2905
+ ({ server, apiKey }) => requestJson(
2906
+ `${server}/api/marketplace/library`,
2907
+ {
2908
+ method: "GET",
2909
+ headers: mergeAutomationHeaders({
2910
+ Authorization: `Bearer ${apiKey}`
2911
+ })
2912
+ }
2913
+ )
2914
+ ),
2915
+ Effect5.map((response) => response.items)
2916
+ );
2917
+ var fetchMarketplaceInstallManifest = (slug, options) => pipe5(
2918
+ Effect5.all({
2919
+ server: resolveServer(options.server),
2920
+ apiKey: resolveApiKey2(options.apiKey)
2921
+ }),
2922
+ Effect5.flatMap(
2923
+ ({ server, apiKey }) => Effect5.all({
2924
+ apiKey: Effect5.succeed(apiKey),
2925
+ server: resolveRequestServer(server, apiKey)
2926
+ })
2927
+ ),
2928
+ Effect5.flatMap(
2929
+ ({ server, apiKey }) => requestJson(
2930
+ `${server}/api/marketplace/install-manifest/${encodeURIComponent(slug)}`,
2931
+ {
2932
+ method: "GET",
2933
+ headers: mergeAutomationHeaders({
2934
+ Authorization: `Bearer ${apiKey}`
2935
+ })
2936
+ },
2937
+ {
2938
+ normalizeErrorMessage: ({ payload }) => payload?.error === "Not entitled" ? "Pack not found or no longer available in your library." : void 0,
2939
+ notFoundMessage: "Pack not found or no longer available in your library."
2940
+ }
2941
+ )
2942
+ )
2943
+ );
2944
+ var fetchMarketplaceLibraryAsync = (options) => Effect5.runPromise(fetchMarketplaceLibrary(options));
2945
+ var fetchMarketplaceInstallManifestAsync = (slug, options) => Effect5.runPromise(fetchMarketplaceInstallManifest(slug, options));
2946
+
2947
+ // src/lib/command-output.ts
2948
+ init_tui();
2949
+ var CommandValidationError = class extends Error {
2950
+ code = "CLI_VALIDATION_ERROR";
2951
+ constructor(message) {
2952
+ super(message);
2953
+ this.name = "CommandValidationError";
2954
+ }
2955
+ };
2956
+ var writeJson = (value) => {
2957
+ process8.stdout.write(`${JSON.stringify(value, null, 2)}
2958
+ `);
2959
+ };
2960
+ var toJsonErrorOutput = (error) => {
2961
+ if (error instanceof ApiError) {
2962
+ return {
2963
+ error: error.message,
2964
+ code: error.code,
2965
+ status: error.status
2966
+ };
2967
+ }
2968
+ if (error instanceof AuthenticationError) {
2969
+ return {
2970
+ error: error.message,
2971
+ code: "AUTH_REQUIRED",
2972
+ status: 401
2973
+ };
2974
+ }
2975
+ if (error instanceof NetworkError) {
2976
+ return {
2977
+ error: error.message,
2978
+ code: "NETWORK_ERROR",
2979
+ status: 503
2980
+ };
2981
+ }
2982
+ if (error instanceof MarketplaceApiError) {
2983
+ return {
2984
+ error: error.message,
2985
+ code: error.code,
2986
+ status: error.status
2987
+ };
2988
+ }
2989
+ if (error instanceof CommandValidationError || error instanceof SyntaxError) {
2990
+ return {
2991
+ error: error.message,
2992
+ code: "CLI_VALIDATION_ERROR",
2993
+ status: 400
2994
+ };
2995
+ }
2996
+ if (error instanceof Error) {
2997
+ return {
2998
+ error: error.message,
2999
+ code: "CLI_ERROR",
3000
+ status: 500
3001
+ };
3002
+ }
3003
+ return {
3004
+ error: String(error),
3005
+ code: "CLI_ERROR",
3006
+ status: 500
3007
+ };
3008
+ };
3009
+ var fail = (message) => {
3010
+ throw new CommandValidationError(message);
3011
+ };
3012
+ var exitCommandError = (error, options) => {
3013
+ if (options?.json) {
3014
+ writeJson(toJsonErrorOutput(error));
3015
+ process8.exit(1);
2276
3016
  }
2277
- const cwd = options.projectRoot ?? process2.cwd();
2278
- return join(cwd, agent.hookProjectConfigPath);
3017
+ const message = error instanceof Error ? error.message : String(error);
3018
+ log.error(message);
3019
+ process8.exit(1);
2279
3020
  };
2280
3021
 
2281
3022
  // src/commands/agents.ts
2282
- init_api();
2283
3023
  init_tui();
2284
3024
  var parseCsv = (input) => {
2285
3025
  if (!input) {
@@ -2288,18 +3028,6 @@ var parseCsv = (input) => {
2288
3028
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2289
3029
  return values.length > 0 ? values : void 0;
2290
3030
  };
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
3031
  var run = (command, args, options) => {
2304
3032
  const apiOptions = {
2305
3033
  ...options.server ? { serverUrl: options.server } : {},
@@ -2403,7 +3131,7 @@ async function agentsListCommand(options) {
2403
3131
  log.success("sub-agents list completed");
2404
3132
  writeJson(result);
2405
3133
  } catch (error) {
2406
- exitWithError(error);
3134
+ exitCommandError(error, { json: options.json });
2407
3135
  }
2408
3136
  }
2409
3137
  async function agentsGetCommand(options) {
@@ -2417,7 +3145,7 @@ async function agentsGetCommand(options) {
2417
3145
  log.success("sub-agents get completed");
2418
3146
  writeJson(result);
2419
3147
  } catch (error) {
2420
- exitWithError(error);
3148
+ exitCommandError(error, { json: options.json });
2421
3149
  }
2422
3150
  }
2423
3151
  async function agentsCreateCommand(options) {
@@ -2445,7 +3173,7 @@ async function agentsCreateCommand(options) {
2445
3173
  }
2446
3174
  log.success("sub-agents create completed");
2447
3175
  } catch (error) {
2448
- exitWithError(error);
3176
+ exitCommandError(error, { json: options.json });
2449
3177
  }
2450
3178
  }
2451
3179
  async function agentsUpdateCommand(options) {
@@ -2470,7 +3198,7 @@ async function agentsUpdateCommand(options) {
2470
3198
  }
2471
3199
  log.success("sub-agents update completed");
2472
3200
  } catch (error) {
2473
- exitWithError(error);
3201
+ exitCommandError(error, { json: options.json });
2474
3202
  }
2475
3203
  }
2476
3204
  async function agentsRemoveCommand(options) {
@@ -2486,7 +3214,7 @@ async function agentsRemoveCommand(options) {
2486
3214
  }
2487
3215
  log.success("sub-agents remove completed");
2488
3216
  } catch (error) {
2489
- exitWithError(error);
3217
+ exitCommandError(error, { json: options.json });
2490
3218
  }
2491
3219
  }
2492
3220
  async function agentsInstallCommand(options) {
@@ -2511,365 +3239,57 @@ async function agentsInstallCommand(options) {
2511
3239
  target,
2512
3240
  [spec],
2513
3241
  installPath
2514
- );
2515
- summary.push({
2516
- platform: target.id,
2517
- installPath,
2518
- written: result.written.length,
2519
- warnings: result.warnings,
2520
- errors: result.errors
2521
- });
2522
- }
2523
- if (options.json) {
2524
- 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}`
2819
- }
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
+ );
3243
+ summary.push({
3244
+ platform: target.id,
3245
+ installPath,
3246
+ written: result.written.length,
3247
+ warnings: result.warnings,
3248
+ errors: result.errors
3249
+ });
2838
3250
  }
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);
3251
+ if (options.json) {
3252
+ writeJson(summary);
3253
+ return;
3254
+ }
3255
+ for (const item of summary) {
3256
+ log.info(
3257
+ `${item.platform}: ${item.written} file(s) written${item.installPath ? ` to ${item.installPath}` : ""}`
3258
+ );
3259
+ for (const warning of item.warnings) {
3260
+ log.warn(` warning: ${warning}`);
3261
+ }
3262
+ for (const error of item.errors) {
3263
+ log.error(` error (${error.agent}): ${error.error}`);
3264
+ }
3265
+ }
3266
+ log.success("sub-agents install completed");
3267
+ } catch (error) {
3268
+ exitCommandError(error, { json: options.json });
2859
3269
  }
2860
3270
  }
2861
- function getDeviceInfo() {
2862
- return {
2863
- deviceName: hostname(),
2864
- deviceOs: platform(),
2865
- deviceHostname: hostname()
2866
- };
2867
- }
2868
3271
 
2869
3272
  // src/commands/auth.ts
3273
+ init_esm_shims();
3274
+ init_api();
3275
+ init_config();
3276
+ init_device_auth();
2870
3277
  init_tui();
2871
- var SESSION_TOKEN_PREFIX = "brs_";
3278
+ import process10 from "process";
3279
+ var SESSION_TOKEN_PREFIX3 = "brs_";
3280
+ var LOOPBACK_HOSTS3 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
2872
3281
  var DEFAULT_TIMEOUT_SECONDS = 300;
3282
+ var resolvePersistedCliServerUrl = (serverUrl, convexSiteUrl) => {
3283
+ try {
3284
+ const convexUrl = new URL(convexSiteUrl);
3285
+ if (LOOPBACK_HOSTS3.has(convexUrl.hostname)) {
3286
+ return convexSiteUrl;
3287
+ }
3288
+ } catch {
3289
+ return serverUrl;
3290
+ }
3291
+ return serverUrl;
3292
+ };
2873
3293
  async function configureDefaultScopeAsync(serverUrl) {
2874
3294
  const shouldConfigureScope = await confirm({
2875
3295
  message: "Configure organization and scope defaults now?",
@@ -2904,9 +3324,9 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2904
3324
  log.error(
2905
3325
  "The API key could not be validated. Please check your key and try again."
2906
3326
  );
2907
- process8.exit(1);
3327
+ process10.exit(1);
2908
3328
  }
2909
- await persistApiKeyAsync(apiKey);
3329
+ await persistApiKeyAsync(apiKey, serverUrl);
2910
3330
  stopAuthSpinner("API key validated and saved");
2911
3331
  if (options.scope !== false) {
2912
3332
  await configureDefaultScopeAsync(serverUrl);
@@ -2919,7 +3339,7 @@ async function manualTokenFlow(apiKey, serverUrl, options) {
2919
3339
  stopAuthSpinner("Validation failed");
2920
3340
  const message = error instanceof Error ? error.message : String(error);
2921
3341
  log.error(`Failed to validate API key: ${message}`);
2922
- process8.exit(1);
3342
+ process10.exit(1);
2923
3343
  }
2924
3344
  }
2925
3345
  function handlePollingError(error) {
@@ -2933,7 +3353,7 @@ function handlePollingError(error) {
2933
3353
  const message = error instanceof Error ? error.message : String(error);
2934
3354
  log.error(`Authentication failed: ${message}`);
2935
3355
  }
2936
- process8.exit(1);
3356
+ process10.exit(1);
2937
3357
  }
2938
3358
  async function deviceFlow(serverUrl, timeoutSeconds, options) {
2939
3359
  const configSpinner = spinner();
@@ -2946,7 +3366,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2946
3366
  configSpinner.stop("Failed to fetch auth configuration");
2947
3367
  const message = error instanceof Error ? error.message : String(error);
2948
3368
  log.error(`Could not connect to server: ${message}`);
2949
- process8.exit(1);
3369
+ process10.exit(1);
2950
3370
  }
2951
3371
  const deviceSpinner = spinner();
2952
3372
  deviceSpinner.start("Initiating device authorization...");
@@ -2961,7 +3381,7 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2961
3381
  deviceSpinner.stop("Failed to initiate device authorization");
2962
3382
  const message = error instanceof Error ? error.message : String(error);
2963
3383
  log.error(`Device authorization failed: ${message}`);
2964
- process8.exit(1);
3384
+ process10.exit(1);
2965
3385
  }
2966
3386
  const verificationUrl = buildAuthVerificationUrlFromBaseUrl(
2967
3387
  authConfig.verificationBaseUrl,
@@ -2995,9 +3415,13 @@ async function deviceFlow(serverUrl, timeoutSeconds, options) {
2995
3415
  pollSpinner.stop("Authentication failed");
2996
3416
  handlePollingError(error);
2997
3417
  }
2998
- await persistApiKeyAsync(session.sessionToken, authConfig.convexSiteUrl);
3418
+ const persistedServerUrl = resolvePersistedCliServerUrl(
3419
+ serverUrl,
3420
+ authConfig.convexSiteUrl
3421
+ );
3422
+ await persistApiKeyAsync(session.sessionToken, persistedServerUrl);
2999
3423
  if (options.scope !== false) {
3000
- await configureDefaultScopeAsync(authConfig.convexSiteUrl);
3424
+ await configureDefaultScopeAsync(persistedServerUrl);
3001
3425
  }
3002
3426
  log.success(`Session saved to ${CONFIG_FILE}`);
3003
3427
  outro(
@@ -3025,12 +3449,12 @@ async function authCommand(options) {
3025
3449
  const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : DEFAULT_TIMEOUT_SECONDS;
3026
3450
  if (Number.isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
3027
3451
  log.error("Invalid timeout value. Must be a positive number of seconds.");
3028
- process8.exit(1);
3452
+ process10.exit(1);
3029
3453
  }
3030
3454
  await deviceFlow(serverUrl, timeoutSeconds, options);
3031
3455
  }
3032
3456
  async function displayTokenSource(masked) {
3033
- if (process8.env.BRAID_API_KEY) {
3457
+ if (process10.env.BRAID_API_KEY) {
3034
3458
  log.info(`Authenticated with key: ${masked}`);
3035
3459
  log.info("Source: BRAID_API_KEY environment variable");
3036
3460
  return;
@@ -3111,7 +3535,7 @@ async function authStatusCommand() {
3111
3535
  );
3112
3536
  return;
3113
3537
  }
3114
- if (config.token.startsWith(SESSION_TOKEN_PREFIX)) {
3538
+ if (config.token.startsWith(SESSION_TOKEN_PREFIX3)) {
3115
3539
  await displaySessionInfo(config.token);
3116
3540
  await displayProjectConfig();
3117
3541
  displayResolvedSettings(config);
@@ -3134,7 +3558,7 @@ async function revokeSessionIfActive(token) {
3134
3558
  async function authLogoutCommand() {
3135
3559
  const config = await loadMergedConfigAsync();
3136
3560
  const { clearApiKeyAsync: clearApiKeyAsync2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3137
- if (config.token?.startsWith(SESSION_TOKEN_PREFIX)) {
3561
+ if (config.token?.startsWith(SESSION_TOKEN_PREFIX3)) {
3138
3562
  await revokeSessionIfActive(config.token);
3139
3563
  }
3140
3564
  await clearApiKeyAsync2();
@@ -3146,7 +3570,6 @@ init_esm_shims();
3146
3570
  init_api();
3147
3571
  init_config();
3148
3572
  init_tui();
3149
- import process9 from "process";
3150
3573
  var parseCsv2 = (input) => {
3151
3574
  if (!input) {
3152
3575
  return void 0;
@@ -3154,15 +3577,6 @@ var parseCsv2 = (input) => {
3154
3577
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
3155
3578
  return values.length > 0 ? values : void 0;
3156
3579
  };
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
3580
  var applyCommonOptions = (base, options) => ({
3167
3581
  ...base,
3168
3582
  ...options.server ? { serverUrl: options.server } : {},
@@ -3189,7 +3603,7 @@ async function projectsListCommand(options) {
3189
3603
  applyCommonOptions({}, options)
3190
3604
  );
3191
3605
  if (options.json) {
3192
- writeJson2(result);
3606
+ writeJson(result);
3193
3607
  return;
3194
3608
  }
3195
3609
  log.info("Personal projects:");
@@ -3211,7 +3625,7 @@ Organization: ${orgProjects.orgName} (${orgProjects.orgId})`);
3211
3625
  }
3212
3626
  }
3213
3627
  } catch (error) {
3214
- exitWithError2(error);
3628
+ exitCommandError(error, { json: options.json });
3215
3629
  }
3216
3630
  }
3217
3631
  var buildRulesRequest = async (options) => {
@@ -3232,7 +3646,7 @@ async function rulesListCommand(options) {
3232
3646
  await buildRulesRequest(options)
3233
3647
  );
3234
3648
  if (options.json) {
3235
- writeJson2(result);
3649
+ writeJson(result);
3236
3650
  return;
3237
3651
  }
3238
3652
  if (result.rules.length === 0) {
@@ -3243,7 +3657,7 @@ async function rulesListCommand(options) {
3243
3657
  log.info(`${rule.id} ${rule.title}`);
3244
3658
  }
3245
3659
  } catch (error) {
3246
- exitWithError2(error);
3660
+ exitCommandError(error, { json: options.json });
3247
3661
  }
3248
3662
  }
3249
3663
  var buildSkillsRequest = async (options) => {
@@ -3263,7 +3677,7 @@ async function skillsListCommand(options) {
3263
3677
  try {
3264
3678
  const result = await fetchSkillsAsync(await buildSkillsRequest(options));
3265
3679
  if (options.json) {
3266
- writeJson2(result);
3680
+ writeJson(result);
3267
3681
  return;
3268
3682
  }
3269
3683
  if (result.skills.length === 0) {
@@ -3274,7 +3688,7 @@ async function skillsListCommand(options) {
3274
3688
  log.info(`${skill.name}`);
3275
3689
  }
3276
3690
  } catch (error) {
3277
- exitWithError2(error);
3691
+ exitCommandError(error, { json: options.json });
3278
3692
  }
3279
3693
  }
3280
3694
 
@@ -3421,11 +3835,11 @@ init_lockfile();
3421
3835
  init_esm_shims();
3422
3836
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
3423
3837
  import { dirname as dirname6, join as join7 } from "path";
3424
- import { Data as Data5, Effect as Effect6, pipe as pipe6 } from "effect";
3838
+ import { Data as Data6, Effect as Effect7, pipe as pipe7 } from "effect";
3425
3839
  var METADATA_FILENAME2 = ".braid-metadata.json";
3426
- var MetadataReadError = class extends Data5.TaggedError("MetadataReadError") {
3840
+ var MetadataReadError = class extends Data6.TaggedError("MetadataReadError") {
3427
3841
  };
3428
- var MetadataWriteError = class extends Data5.TaggedError("MetadataWriteError") {
3842
+ var MetadataWriteError = class extends Data6.TaggedError("MetadataWriteError") {
3429
3843
  };
3430
3844
  var normalizeInstalledSkill = (skill) => ({
3431
3845
  kind: skill.kind ?? "skill",
@@ -3441,18 +3855,18 @@ var normalizeMetadata = (metadata) => ({
3441
3855
  var getMetadataPath = (skillsDir) => join7(dirname6(skillsDir), METADATA_FILENAME2);
3442
3856
  var readMetadata = (skillsDir) => {
3443
3857
  const metadataPath = getMetadataPath(skillsDir);
3444
- return pipe6(
3445
- Effect6.tryPromise({
3858
+ return pipe7(
3859
+ Effect7.tryPromise({
3446
3860
  try: () => readFile4(metadataPath, "utf-8"),
3447
3861
  catch: (e) => new MetadataReadError({ path: metadataPath, cause: e })
3448
3862
  }),
3449
- Effect6.flatMap(
3450
- (content) => Effect6.try({
3863
+ Effect7.flatMap(
3864
+ (content) => Effect7.try({
3451
3865
  try: () => normalizeMetadata(JSON.parse(content)),
3452
3866
  catch: () => normalizeMetadata(void 0)
3453
3867
  })
3454
3868
  ),
3455
- Effect6.orElseSucceed(() => normalizeMetadata(void 0))
3869
+ Effect7.orElseSucceed(() => normalizeMetadata(void 0))
3456
3870
  );
3457
3871
  };
3458
3872
  var readRawJson = async (path2) => {
@@ -3469,7 +3883,7 @@ var readRawJson = async (path2) => {
3469
3883
  };
3470
3884
  var writeMetadata = (skillsDir, metadata) => {
3471
3885
  const metadataPath = getMetadataPath(skillsDir);
3472
- return Effect6.tryPromise({
3886
+ return Effect7.tryPromise({
3473
3887
  try: async () => {
3474
3888
  const existing = await readRawJson(metadataPath);
3475
3889
  const merged = { ...existing, skills: metadata.skills };
@@ -3478,9 +3892,9 @@ var writeMetadata = (skillsDir, metadata) => {
3478
3892
  catch: (e) => new MetadataWriteError({ path: metadataPath, cause: e })
3479
3893
  });
3480
3894
  };
3481
- var updateMetadata = (skillsDir, newSkills) => pipe6(
3895
+ var updateMetadata = (skillsDir, newSkills) => pipe7(
3482
3896
  readMetadata(skillsDir),
3483
- Effect6.map((existing) => {
3897
+ Effect7.map((existing) => {
3484
3898
  const now = (/* @__PURE__ */ new Date()).toISOString();
3485
3899
  const updatedSkills = [...existing.skills];
3486
3900
  for (const skill of newSkills) {
@@ -3503,35 +3917,35 @@ var updateMetadata = (skillsDir, newSkills) => pipe6(
3503
3917
  }
3504
3918
  return { skills: updatedSkills };
3505
3919
  }),
3506
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3920
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3507
3921
  );
3508
- var removeFromMetadata = (skillsDir, skillName) => pipe6(
3922
+ var removeFromMetadata = (skillsDir, skillName) => pipe7(
3509
3923
  readMetadata(skillsDir),
3510
- Effect6.map((metadata) => ({
3924
+ Effect7.map((metadata) => ({
3511
3925
  skills: metadata.skills.filter((s) => s.name !== skillName)
3512
3926
  })),
3513
- Effect6.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3927
+ Effect7.flatMap((metadata) => writeMetadata(skillsDir, metadata))
3514
3928
  );
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));
3929
+ var readMetadataAsync = (skillsDir) => Effect7.runPromise(readMetadata(skillsDir));
3930
+ var updateMetadataAsync = (skillsDir, newSkills) => Effect7.runPromise(updateMetadata(skillsDir, newSkills));
3931
+ var removeFromMetadataAsync = (skillsDir, skillName) => Effect7.runPromise(removeFromMetadata(skillsDir, skillName));
3518
3932
 
3519
3933
  // src/lib/rule-writer.ts
3520
3934
  init_esm_shims();
3521
3935
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
3522
3936
  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") {
3937
+ import { Data as Data7, Effect as Effect8, pipe as pipe8 } from "effect";
3938
+ var RuleWriteError = class extends Data7.TaggedError("RuleWriteError") {
3525
3939
  };
3526
- var createDirectory = (dir) => Effect7.tryPromise({
3940
+ var createDirectory = (dir) => Effect8.tryPromise({
3527
3941
  try: () => mkdir4(dir, { recursive: true }),
3528
3942
  catch: (e) => new RuleWriteError({ path: dir, operation: "mkdir", cause: e })
3529
3943
  });
3530
- var writeTextFile = (fullPath, content) => Effect7.tryPromise({
3944
+ var writeTextFile = (fullPath, content) => Effect8.tryPromise({
3531
3945
  try: () => writeFile6(fullPath, content, "utf-8"),
3532
3946
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "write", cause: e })
3533
3947
  });
3534
- var readTextFile = (fullPath) => Effect7.tryPromise({
3948
+ var readTextFile = (fullPath) => Effect8.tryPromise({
3535
3949
  try: () => readFile5(fullPath, "utf-8"),
3536
3950
  catch: (e) => new RuleWriteError({ path: fullPath, operation: "read", cause: e })
3537
3951
  });
@@ -3539,7 +3953,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3539
3953
  const resolvedBase = resolve3(basePath);
3540
3954
  const resolvedFull = resolve3(basePath, ruleName);
3541
3955
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep2)) {
3542
- return Effect7.fail(
3956
+ return Effect8.fail(
3543
3957
  new RuleWriteError({
3544
3958
  path: ruleName,
3545
3959
  operation: "write",
@@ -3549,7 +3963,7 @@ var assertRulePathWithinBase = (basePath, ruleName) => {
3549
3963
  })
3550
3964
  );
3551
3965
  }
3552
- return Effect7.succeed(resolvedFull);
3966
+ return Effect8.succeed(resolvedFull);
3553
3967
  };
3554
3968
  var BRAID_SECTION_START = "<!-- braid:rules:start -->";
3555
3969
  var BRAID_SECTION_END = "<!-- braid:rules:end -->";
@@ -3582,30 +3996,30 @@ function buildAppendContent(rules2) {
3582
3996
  lines.push(BRAID_SECTION_END);
3583
3997
  return lines.join("\n");
3584
3998
  }
3585
- var writeMdcRules = (basePath, rules2) => pipe7(
3999
+ var writeMdcRules = (basePath, rules2) => pipe8(
3586
4000
  createDirectory(basePath),
3587
- Effect7.flatMap(
3588
- () => Effect7.forEach(
4001
+ Effect8.flatMap(
4002
+ () => Effect8.forEach(
3589
4003
  rules2,
3590
- (rule) => pipe7(
4004
+ (rule) => pipe8(
3591
4005
  assertRulePathWithinBase(basePath, `${rule.name}.mdc`),
3592
- Effect7.flatMap(
4006
+ Effect8.flatMap(
3593
4007
  (filePath) => writeTextFile(filePath, buildMdcContent(rule))
3594
4008
  )
3595
4009
  ),
3596
4010
  { concurrency: "unbounded" }
3597
4011
  )
3598
4012
  ),
3599
- Effect7.asVoid
4013
+ Effect8.asVoid
3600
4014
  );
3601
- var writeMarkdownDirRules = (basePath, rules2) => pipe7(
4015
+ var writeMarkdownDirRules = (basePath, rules2) => pipe8(
3602
4016
  createDirectory(basePath),
3603
- Effect7.flatMap(
3604
- () => Effect7.forEach(
4017
+ Effect8.flatMap(
4018
+ () => Effect8.forEach(
3605
4019
  rules2,
3606
- (rule) => pipe7(
4020
+ (rule) => pipe8(
3607
4021
  assertRulePathWithinBase(basePath, `${rule.name}.md`),
3608
- Effect7.flatMap(
4022
+ Effect8.flatMap(
3609
4023
  (filePath) => writeTextFile(filePath, `# ${rule.title}
3610
4024
 
3611
4025
  ${rule.content}`)
@@ -3614,17 +4028,17 @@ ${rule.content}`)
3614
4028
  { concurrency: "unbounded" }
3615
4029
  )
3616
4030
  ),
3617
- Effect7.asVoid
4031
+ Effect8.asVoid
3618
4032
  );
3619
- var writeAppendSingleRules = (filePath, rules2) => pipe7(
4033
+ var writeAppendSingleRules = (filePath, rules2) => pipe8(
3620
4034
  createDirectory(dirname7(filePath)),
3621
- Effect7.flatMap(
3622
- () => pipe7(
4035
+ Effect8.flatMap(
4036
+ () => pipe8(
3623
4037
  readTextFile(filePath),
3624
- Effect7.orElseSucceed(() => "")
4038
+ Effect8.orElseSucceed(() => "")
3625
4039
  )
3626
4040
  ),
3627
- Effect7.flatMap((existing) => {
4041
+ Effect8.flatMap((existing) => {
3628
4042
  const braidContent = buildAppendContent(rules2);
3629
4043
  const startIdx = existing.indexOf(BRAID_SECTION_START);
3630
4044
  const endIdx = existing.indexOf(BRAID_SECTION_END);
@@ -3648,21 +4062,21 @@ var writeRulesForFormat = (basePath, rules2, format) => {
3648
4062
  case "append-single":
3649
4063
  return writeAppendSingleRules(basePath, rules2);
3650
4064
  default:
3651
- return Effect7.void;
4065
+ return Effect8.void;
3652
4066
  }
3653
4067
  };
3654
4068
  var writeRulesForAgent = (agent, rules2, rulesPath) => {
3655
4069
  if (!agent.ruleFormat || rules2.length === 0) {
3656
- return Effect7.succeed({ written: 0, errors: [] });
4070
+ return Effect8.succeed({ written: 0, errors: [] });
3657
4071
  }
3658
- return pipe7(
4072
+ return pipe8(
3659
4073
  writeRulesForFormat(rulesPath, rules2, agent.ruleFormat),
3660
- Effect7.map(() => ({
4074
+ Effect8.map(() => ({
3661
4075
  written: rules2.length,
3662
4076
  errors: []
3663
4077
  })),
3664
- Effect7.catch(
3665
- (error) => Effect7.succeed({
4078
+ Effect8.catch(
4079
+ (error) => Effect8.succeed({
3666
4080
  written: 0,
3667
4081
  errors: [
3668
4082
  {
@@ -3674,29 +4088,29 @@ var writeRulesForAgent = (agent, rules2, rulesPath) => {
3674
4088
  )
3675
4089
  );
3676
4090
  };
3677
- var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect7.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
4091
+ var writeRulesForAgentAsync = (agent, rules2, rulesPath) => Effect8.runPromise(writeRulesForAgent(agent, rules2, rulesPath));
3678
4092
 
3679
4093
  // src/lib/skill-writer.ts
3680
4094
  init_esm_shims();
3681
4095
  import { createHash as createHash2 } from "crypto";
3682
4096
  import { chmod, mkdir as mkdir5, rm as rm2, symlink, writeFile as writeFile7 } from "fs/promises";
3683
4097
  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") {
4098
+ import { Data as Data8, Effect as Effect9, pipe as pipe9 } from "effect";
4099
+ var WriteError = class extends Data8.TaggedError("WriteError") {
3686
4100
  };
3687
- var createDirectory2 = (dir, fullPath) => Effect8.tryPromise({
4101
+ var createDirectory2 = (dir, fullPath) => Effect9.tryPromise({
3688
4102
  try: () => mkdir5(dir, { recursive: true }),
3689
4103
  catch: (e) => new WriteError({ path: fullPath, operation: "mkdir", cause: e })
3690
4104
  });
3691
- var writeTextFile2 = (fullPath, content) => Effect8.tryPromise({
4105
+ var writeTextFile2 = (fullPath, content) => Effect9.tryPromise({
3692
4106
  try: () => writeFile7(fullPath, content, "utf-8"),
3693
4107
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3694
4108
  });
3695
- var writeBinaryFile = (fullPath, content) => Effect8.tryPromise({
4109
+ var writeBinaryFile = (fullPath, content) => Effect9.tryPromise({
3696
4110
  try: () => writeFile7(fullPath, content),
3697
4111
  catch: (e) => new WriteError({ path: fullPath, operation: "write", cause: e })
3698
4112
  });
3699
- var makeExecutable = (fullPath) => Effect8.tryPromise({
4113
+ var makeExecutable = (fullPath) => Effect9.tryPromise({
3700
4114
  try: () => chmod(fullPath, 493),
3701
4115
  catch: (e) => new WriteError({ path: fullPath, operation: "chmod", cause: e })
3702
4116
  });
@@ -3704,7 +4118,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3704
4118
  const resolvedBase = resolve4(basePath);
3705
4119
  const resolvedFull = resolve4(basePath, untrustedPath);
3706
4120
  if (resolvedFull !== resolvedBase && !resolvedFull.startsWith(resolvedBase + sep3)) {
3707
- return Effect8.fail(
4121
+ return Effect9.fail(
3708
4122
  new WriteError({
3709
4123
  path: untrustedPath,
3710
4124
  operation: "write",
@@ -3714,7 +4128,7 @@ var assertWithinBase2 = (basePath, untrustedPath) => {
3714
4128
  })
3715
4129
  );
3716
4130
  }
3717
- return Effect8.succeed(resolvedFull);
4131
+ return Effect9.succeed(resolvedFull);
3718
4132
  };
3719
4133
  var COMPATIBILITY_REGEX = /^compatibility:\s*.+$/m;
3720
4134
  var rewriteCompatibility = (content, agentId) => {
@@ -3757,22 +4171,22 @@ var isScriptFile = (path2) => {
3757
4171
  return inScriptsDir && scriptExtensions.some((ext) => lowerPath.endsWith(ext));
3758
4172
  };
3759
4173
  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(
4174
+ var setExecutableIfScript = (fullPath, file, agentId) => isScriptFile(file.path) && decodeFileContent(file, agentId).startsWith("#!") ? makeExecutable(fullPath) : Effect9.void;
4175
+ var writeSkillFile = (basePath, file, agentId) => pipe9(
3762
4176
  assertWithinBase2(basePath, file.path),
3763
- Effect8.flatMap((fullPath) => {
4177
+ Effect9.flatMap((fullPath) => {
3764
4178
  const dir = dirname8(fullPath);
3765
- return pipe8(
4179
+ return pipe9(
3766
4180
  createDirectory2(dir, fullPath),
3767
- Effect8.flatMap(() => writeFileContent(fullPath, file, agentId)),
3768
- Effect8.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
4181
+ Effect9.flatMap(() => writeFileContent(fullPath, file, agentId)),
4182
+ Effect9.flatMap(() => setExecutableIfScript(fullPath, file, agentId))
3769
4183
  );
3770
4184
  })
3771
4185
  );
3772
- var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
4186
+ var writeSkillAtPath = (skillPath, skill, agentId) => pipe9(
3773
4187
  createDirectory2(skillPath, skillPath),
3774
- Effect8.flatMap(
3775
- () => Effect8.forEach(
4188
+ Effect9.flatMap(
4189
+ () => Effect9.forEach(
3776
4190
  skill.files,
3777
4191
  (file) => writeSkillFile(skillPath, file, agentId),
3778
4192
  {
@@ -3780,16 +4194,16 @@ var writeSkillAtPath = (skillPath, skill, agentId) => pipe8(
3780
4194
  }
3781
4195
  )
3782
4196
  ),
3783
- Effect8.map(() => void 0)
4197
+ Effect9.map(() => void 0)
3784
4198
  );
3785
- var writeSkill = (basePath, skill, agentId) => pipe8(
4199
+ var writeSkill = (basePath, skill, agentId) => pipe9(
3786
4200
  assertWithinBase2(basePath, skill.name),
3787
- Effect8.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
4201
+ Effect9.flatMap((skillDir) => writeSkillAtPath(skillDir, skill, agentId))
3788
4202
  );
3789
- var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
4203
+ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe9(
3790
4204
  assertWithinBase2(basePath, skill.name),
3791
- Effect8.flatMap(
3792
- (installPath) => Effect8.tryPromise({
4205
+ Effect9.flatMap(
4206
+ (installPath) => Effect9.tryPromise({
3793
4207
  try: async () => {
3794
4208
  if (!options.storeRoot) {
3795
4209
  throw new Error("storeRoot is required for shared-store installs");
@@ -3799,7 +4213,7 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3799
4213
  skill,
3800
4214
  agentId
3801
4215
  );
3802
- await Effect8.runPromise(
4216
+ await Effect9.runPromise(
3803
4217
  writeSkillAtPath(storedBundlePath, skill, agentId)
3804
4218
  );
3805
4219
  const destinationPath = await resolveManagedBundlePathAsync(
@@ -3826,14 +4240,14 @@ var writeSkillSymlink = (basePath, skill, agentId, options) => pipe8(
3826
4240
  );
3827
4241
  var hashSkill = (skill, agentId) => createHash2("sha256").update(JSON.stringify({ agentId, skill })).digest("hex").slice(0, 16);
3828
4242
  var resolveStorePath = (storeRoot, skill, agentId) => join8(storeRoot, `${skill.name}-${hashSkill(skill, agentId)}`);
3829
- var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3830
- Effect8.forEach(
4243
+ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe9(
4244
+ Effect9.forEach(
3831
4245
  skills2,
3832
- (skill) => pipe8(
4246
+ (skill) => pipe9(
3833
4247
  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({
4248
+ Effect9.map(() => ({ success: true, skill: skill.name })),
4249
+ Effect9.catch(
4250
+ (error) => Effect9.succeed({
3837
4251
  success: false,
3838
4252
  skill: skill.name,
3839
4253
  error: error.cause instanceof Error ? error.cause.message : String(error.cause)
@@ -3842,14 +4256,14 @@ var writeSkills = (basePath, skills2, agentId, options = {}) => pipe8(
3842
4256
  ),
3843
4257
  { concurrency: "unbounded" }
3844
4258
  ),
3845
- Effect8.map((results) => ({
4259
+ Effect9.map((results) => ({
3846
4260
  written: results.filter((r) => r.success).map((r) => r.skill),
3847
4261
  errors: results.filter(
3848
4262
  (r) => !r.success
3849
4263
  ).map((r) => ({ skill: r.skill, error: r.error }))
3850
4264
  }))
3851
4265
  );
3852
- var writeSkillsAsync = (basePath, skills2, agentId, options) => Effect8.runPromise(writeSkills(basePath, skills2, agentId, options));
4266
+ var writeSkillsAsync = (basePath, skills2, agentId, options) => Effect9.runPromise(writeSkills(basePath, skills2, agentId, options));
3853
4267
 
3854
4268
  // src/commands/install.ts
3855
4269
  init_tui();
@@ -4219,7 +4633,7 @@ function showInstallOutro(totalWritten, totalErrors, suffix) {
4219
4633
  outro(`Successfully installed ${totalWritten} items${suffix}.`);
4220
4634
  }
4221
4635
  }
4222
- async function fetchPublicContent(handle, slug, version, lockedRuleIds, options, installSpinner) {
4636
+ async function fetchPublicContent(handle, slug, version, lockedRuleIds, apiKey, options, installSpinner) {
4223
4637
  if (version !== void 0 && lockedRuleIds) {
4224
4638
  installSpinner.stop(`Installing pinned version v${version}`);
4225
4639
  installSpinner.start("Downloading content...");
@@ -4229,7 +4643,8 @@ async function fetchPublicContent(handle, slug, version, lockedRuleIds, options,
4229
4643
  slug,
4230
4644
  lockedRuleIds,
4231
4645
  options.server,
4232
- version
4646
+ version,
4647
+ apiKey
4233
4648
  ),
4234
4649
  ruleIds: lockedRuleIds
4235
4650
  };
@@ -4238,7 +4653,8 @@ async function fetchPublicContent(handle, slug, version, lockedRuleIds, options,
4238
4653
  handle,
4239
4654
  slug,
4240
4655
  options.server,
4241
- version
4656
+ version,
4657
+ apiKey
4242
4658
  );
4243
4659
  installSpinner.stop("Found public content");
4244
4660
  displayPublicMetadata(metadata);
@@ -4250,7 +4666,8 @@ async function fetchPublicContent(handle, slug, version, lockedRuleIds, options,
4250
4666
  slug,
4251
4667
  ruleIds,
4252
4668
  options.server,
4253
- version
4669
+ version,
4670
+ apiKey
4254
4671
  ),
4255
4672
  ruleIds
4256
4673
  };
@@ -4299,6 +4716,7 @@ async function writeLockfileAfterInstall(handle, slug, version, contentHash, rul
4299
4716
  }
4300
4717
  }
4301
4718
  async function publicInstallCommand(source, options) {
4719
+ const config = await loadMergedConfigAsync();
4302
4720
  const parsed = parsePublicSource(source);
4303
4721
  if (!parsed) {
4304
4722
  log.error(`Invalid public source: ${source}`);
@@ -4322,6 +4740,7 @@ async function publicInstallCommand(source, options) {
4322
4740
  log.info(`Using locked version: v${version}`);
4323
4741
  }
4324
4742
  const versionSuffix = version !== void 0 ? `@${version}` : "";
4743
+ const serverUrl = options.server ?? config.serverUrl;
4325
4744
  intro(`Installing from @${handle}/${slug}${versionSuffix}`);
4326
4745
  const installSpinner = spinner();
4327
4746
  installSpinner.start("Fetching public content...");
@@ -4331,7 +4750,11 @@ async function publicInstallCommand(source, options) {
4331
4750
  slug,
4332
4751
  version,
4333
4752
  lockedRuleIds,
4334
- options,
4753
+ config.token,
4754
+ {
4755
+ ...options,
4756
+ server: serverUrl
4757
+ },
4335
4758
  installSpinner
4336
4759
  );
4337
4760
  installSpinner.stop(`Downloaded ${summarizeContent(response)}`);
@@ -4339,11 +4762,13 @@ async function publicInstallCommand(source, options) {
4339
4762
  log.warn("No content to install.");
4340
4763
  process.exit(0);
4341
4764
  }
4342
- const serverUrl = options.server ?? "https://braid.cloud";
4343
4765
  const { totalWritten, totalErrors } = await installToSelectedAgents(
4344
4766
  response,
4345
4767
  serverUrl,
4346
- options,
4768
+ {
4769
+ ...options,
4770
+ server: serverUrl
4771
+ },
4347
4772
  installSpinner
4348
4773
  );
4349
4774
  if (totalWritten > 0 && version !== void 0) {
@@ -4540,7 +4965,8 @@ async function listCommand(options) {
4540
4965
 
4541
4966
  // src/commands/manage.ts
4542
4967
  init_esm_shims();
4543
- import process10 from "process";
4968
+ init_device_auth();
4969
+ import process11 from "process";
4544
4970
 
4545
4971
  // src/lib/manage-server.ts
4546
4972
  init_esm_shims();
@@ -4550,6 +4976,7 @@ import { createServer } from "http";
4550
4976
  import { isAbsolute, relative as relative2, resolve as resolve6 } from "path";
4551
4977
  import { fileURLToPath as fileURLToPath2 } from "url";
4552
4978
  init_config();
4979
+ init_device_auth();
4553
4980
 
4554
4981
  // src/lib/manage-actions.ts
4555
4982
  init_esm_shims();
@@ -4560,8 +4987,8 @@ import { basename as basename2, dirname as dirname10, join as join11, relative }
4560
4987
  init_esm_shims();
4561
4988
  import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile8 } from "fs/promises";
4562
4989
  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") {
4990
+ import { Data as Data9, Effect as Effect10 } from "effect";
4991
+ var HookConfigWriteError = class extends Data9.TaggedError("HookConfigWriteError") {
4565
4992
  };
4566
4993
  var HOOK_METADATA_FILENAME = ".braid-metadata.json";
4567
4994
  function stableStringify(value) {
@@ -4886,7 +5313,7 @@ async function ensureMarketplaceHookBundleFile(settingsPath, sourceSlug) {
4886
5313
  });
4887
5314
  return bundlePath;
4888
5315
  }
4889
- var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise({
5316
+ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect10.tryPromise({
4890
5317
  try: async () => {
4891
5318
  const activeHooks = hooks.filter(
4892
5319
  (hook) => hook.enabled !== false && hook.runtime === "claude"
@@ -4941,131 +5368,18 @@ var writeMarketplaceHooks = (settingsPath, hooks, options) => Effect9.tryPromise
4941
5368
  },
4942
5369
  catch: (cause) => new HookConfigWriteError({ path: settingsPath, cause })
4943
5370
  });
4944
- var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect9.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
5371
+ var writeMarketplaceHooksAsync = (settingsPath, hooks, options) => Effect10.runPromise(writeMarketplaceHooks(settingsPath, hooks, options));
4945
5372
  var removeMarketplaceHooksBySourceAsync = (settingsPath, sourceSlug) => removeMarketplaceHooksBySource(settingsPath, sourceSlug);
4946
5373
  var ensureMarketplaceHookBundleFileAsync = (settingsPath, sourceSlug) => ensureMarketplaceHookBundleFile(settingsPath, sourceSlug);
4947
5374
  var listInstalledMarketplaceHookSourcesAsync = (settingsPath) => listInstalledMarketplaceHookSources(settingsPath);
4948
5375
  var readMarketplaceHookBundleFileAsync = (bundlePath) => readMarketplaceHookBundleFile(bundlePath);
4949
5376
 
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
5377
  // src/lib/marketplace-installer.ts
5066
5378
  init_esm_shims();
5067
5379
  import { rm as rm4 } from "fs/promises";
5068
5380
  import { join as join10 } from "path";
5381
+ init_config();
5382
+ var TRAILING_SLASH_REGEX3 = /\/$/;
5069
5383
  function parseAgentIds(value) {
5070
5384
  if (!value) {
5071
5385
  return [];
@@ -5135,7 +5449,7 @@ async function installSkillsForAgent(args) {
5135
5449
  versionId: args.manifest.versionId
5136
5450
  },
5137
5451
  version: args.manifest.versionId ?? args.manifest.commitSha ?? "unknown",
5138
- serverUrl: "https://braid.cloud"
5452
+ serverUrl: args.serverUrl
5139
5453
  }))
5140
5454
  );
5141
5455
  args.targets.push(`${args.agent.name} -> ${installPath}`);
@@ -5224,6 +5538,7 @@ async function installAllForAgent(args) {
5224
5538
  agent: args.agent,
5225
5539
  manifest: args.manifest,
5226
5540
  slug: args.slug,
5541
+ serverUrl: args.serverUrl,
5227
5542
  skills: args.skills,
5228
5543
  global: args.global,
5229
5544
  installedSkills: args.installedSkills,
@@ -5281,6 +5596,7 @@ async function installMarketplaceSkillSet(options) {
5281
5596
  "No compatible agents found. Use --agents to specify targets."
5282
5597
  );
5283
5598
  }
5599
+ const serverUrl = (options.serverUrl ?? await getServerUrlAsync().catch(() => "https://braid.cloud")).replace(TRAILING_SLASH_REGEX3, "");
5284
5600
  const targets = [];
5285
5601
  const installedSkills = /* @__PURE__ */ new Set();
5286
5602
  for (const agent of targetAgents) {
@@ -5288,6 +5604,7 @@ async function installMarketplaceSkillSet(options) {
5288
5604
  agent,
5289
5605
  manifest: options.manifest,
5290
5606
  slug: options.slug,
5607
+ serverUrl,
5291
5608
  skills: resolvedSkills,
5292
5609
  rules: rules2,
5293
5610
  agents: agents2,
@@ -5437,7 +5754,8 @@ async function installLibraryPackAsync(options) {
5437
5754
  agents: options.agentId,
5438
5755
  global: options.scope === "global",
5439
5756
  manifest,
5440
- slug: options.slug
5757
+ slug: options.slug,
5758
+ serverUrl: options.server
5441
5759
  });
5442
5760
  }
5443
5761
 
@@ -5914,7 +6232,8 @@ var MIME_TYPES = {
5914
6232
  };
5915
6233
  var TRAILING_SLASHES3 = /\/+$/;
5916
6234
  var MANAGE_AUTH_TIMEOUT_SECONDS = 300;
5917
- var SESSION_TOKEN_PREFIX2 = "brs_";
6235
+ var SESSION_TOKEN_PREFIX4 = "brs_";
6236
+ var LOOPBACK_HOSTS4 = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
5918
6237
  function getErrorStatus(error) {
5919
6238
  if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") {
5920
6239
  return error.status ?? 500;
@@ -5991,7 +6310,18 @@ function getContentType(pathname) {
5991
6310
  const extension = pathname.slice(pathname.lastIndexOf("."));
5992
6311
  return MIME_TYPES[extension] ?? "application/octet-stream";
5993
6312
  }
5994
- function writeJson3(response, status, body) {
6313
+ function resolvePersistedCliServerUrl2(serverUrl, convexSiteUrl) {
6314
+ try {
6315
+ const convexUrl = new URL(convexSiteUrl);
6316
+ if (LOOPBACK_HOSTS4.has(convexUrl.hostname)) {
6317
+ return convexSiteUrl;
6318
+ }
6319
+ } catch {
6320
+ return serverUrl;
6321
+ }
6322
+ return serverUrl;
6323
+ }
6324
+ function writeJson2(response, status, body) {
5995
6325
  response.writeHead(status, {
5996
6326
  "content-type": "application/json; charset=utf-8",
5997
6327
  "cache-control": "no-store"
@@ -6015,7 +6345,7 @@ async function serveStaticAsset(serverResponse, assetRoot, pathname) {
6015
6345
  const assetPath = resolve6(assetRoot, relativePath);
6016
6346
  const resolvedRoot = resolve6(assetRoot);
6017
6347
  if (!isWithinManageAssetRoot(resolvedRoot, assetPath)) {
6018
- writeJson3(serverResponse, 404, { error: "Not found" });
6348
+ writeJson2(serverResponse, 404, { error: "Not found" });
6019
6349
  return true;
6020
6350
  }
6021
6351
  try {
@@ -6109,19 +6439,19 @@ async function handleManageApiRoute(request, response, dependencies) {
6109
6439
  const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
6110
6440
  switch (`${method} ${pathname}`) {
6111
6441
  case "GET /api/inventory":
6112
- writeJson3(response, 200, await dependencies.getInventory());
6442
+ writeJson2(response, 200, await dependencies.getInventory());
6113
6443
  return true;
6114
6444
  case "GET /api/library":
6115
- writeJson3(response, 200, await dependencies.listLibrary());
6445
+ writeJson2(response, 200, await dependencies.listLibrary());
6116
6446
  return true;
6117
6447
  case "GET /api/auth/session":
6118
- writeJson3(response, 200, {
6448
+ writeJson2(response, 200, {
6119
6449
  ok: true,
6120
6450
  result: await dependencies.getAuthSession()
6121
6451
  });
6122
6452
  return true;
6123
6453
  case "POST /api/auth/start":
6124
- writeJson3(response, 200, {
6454
+ writeJson2(response, 200, {
6125
6455
  ok: true,
6126
6456
  result: await dependencies.startAuth()
6127
6457
  });
@@ -6135,7 +6465,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6135
6465
  "AUTH_REQUEST_REQUIRED"
6136
6466
  );
6137
6467
  }
6138
- writeJson3(response, 200, {
6468
+ writeJson2(response, 200, {
6139
6469
  ok: true,
6140
6470
  result: await dependencies.completeAuth({
6141
6471
  requestId: payload.requestId
@@ -6144,14 +6474,14 @@ async function handleManageApiRoute(request, response, dependencies) {
6144
6474
  return true;
6145
6475
  }
6146
6476
  case "POST /api/auth/sign-out":
6147
- writeJson3(response, 200, {
6477
+ writeJson2(response, 200, {
6148
6478
  ok: true,
6149
6479
  result: await dependencies.signOut()
6150
6480
  });
6151
6481
  return true;
6152
6482
  case "POST /api/actions/install": {
6153
6483
  const payload = await readJsonBody(request);
6154
- writeJson3(response, 200, {
6484
+ writeJson2(response, 200, {
6155
6485
  ok: true,
6156
6486
  result: await dependencies.installLibraryPack(payload)
6157
6487
  });
@@ -6159,7 +6489,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6159
6489
  }
6160
6490
  case "POST /api/actions/disable": {
6161
6491
  const payload = await readJsonBody(request);
6162
- writeJson3(response, 200, {
6492
+ writeJson2(response, 200, {
6163
6493
  ok: true,
6164
6494
  result: await dependencies.disableManagedBundle(payload)
6165
6495
  });
@@ -6167,7 +6497,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6167
6497
  }
6168
6498
  case "POST /api/actions/enable": {
6169
6499
  const payload = await readJsonBody(request);
6170
- writeJson3(response, 200, {
6500
+ writeJson2(response, 200, {
6171
6501
  ok: true,
6172
6502
  result: await dependencies.enableManagedBundle(payload)
6173
6503
  });
@@ -6175,7 +6505,7 @@ async function handleManageApiRoute(request, response, dependencies) {
6175
6505
  }
6176
6506
  case "POST /api/actions/remove": {
6177
6507
  const payload = await readJsonBody(request);
6178
- writeJson3(response, 200, {
6508
+ writeJson2(response, 200, {
6179
6509
  ok: true,
6180
6510
  result: await dependencies.removeManagedBundle(payload)
6181
6511
  });
@@ -6197,11 +6527,11 @@ async function handleManageRequest(request, response, assetRoot, dependencies) {
6197
6527
  return;
6198
6528
  }
6199
6529
  }
6200
- writeJson3(response, 404, { error: "Not found" });
6530
+ writeJson2(response, 404, { error: "Not found" });
6201
6531
  } catch (error) {
6202
6532
  const message = error instanceof Error ? error.message : String(error);
6203
6533
  const code = getErrorCode(error);
6204
- writeJson3(response, getErrorStatus(error), {
6534
+ writeJson2(response, getErrorStatus(error), {
6205
6535
  ...code ? { code } : {},
6206
6536
  error: message
6207
6537
  });
@@ -6272,6 +6602,10 @@ async function startManageServer(options = {}) {
6272
6602
  deviceAuth.user_code
6273
6603
  );
6274
6604
  authRequests.set(requestId, {
6605
+ serverUrl: resolvePersistedCliServerUrl2(
6606
+ serverUrl,
6607
+ authConfig.convexSiteUrl
6608
+ ),
6275
6609
  convexSiteUrl: authConfig.convexSiteUrl,
6276
6610
  deviceCode: deviceAuth.device_code,
6277
6611
  expiresIn: deviceAuth.expires_in,
@@ -6304,7 +6638,7 @@ async function startManageServer(options = {}) {
6304
6638
  );
6305
6639
  await persistApiKeyAsync(
6306
6640
  session.sessionToken,
6307
- pendingRequest.convexSiteUrl
6641
+ pendingRequest.serverUrl
6308
6642
  );
6309
6643
  return {
6310
6644
  authenticated: true,
@@ -6321,7 +6655,7 @@ async function startManageServer(options = {}) {
6321
6655
  if (!token) {
6322
6656
  return { authenticated: false };
6323
6657
  }
6324
- if (!token.startsWith(SESSION_TOKEN_PREFIX2)) {
6658
+ if (!token.startsWith(SESSION_TOKEN_PREFIX4)) {
6325
6659
  return { authenticated: true };
6326
6660
  }
6327
6661
  const serverUrl = (options.server ?? config.serverUrl).replace(
@@ -6346,7 +6680,7 @@ async function startManageServer(options = {}) {
6346
6680
  const signOut = options.signOut ?? (async () => {
6347
6681
  const config = await loadMergedConfigAsync();
6348
6682
  const token = config.token;
6349
- if (token?.startsWith(SESSION_TOKEN_PREFIX2)) {
6683
+ if (token?.startsWith(SESSION_TOKEN_PREFIX4)) {
6350
6684
  try {
6351
6685
  const serverUrl = (options.server ?? config.serverUrl).replace(
6352
6686
  TRAILING_SLASHES3,
@@ -6378,7 +6712,7 @@ async function startManageServer(options = {}) {
6378
6712
  handleManageRequest(request, response, assetRoot, dependencies).catch(
6379
6713
  (error) => {
6380
6714
  const message = error instanceof Error ? error.message : String(error);
6381
- writeJson3(response, 500, { error: message });
6715
+ writeJson2(response, 500, { error: message });
6382
6716
  }
6383
6717
  );
6384
6718
  });
@@ -6422,7 +6756,7 @@ async function manageCommand(options) {
6422
6756
  const server = await startManageServer({
6423
6757
  apiKey: options.apiKey,
6424
6758
  port: parsePort(options.port),
6425
- projectRoot: process10.cwd(),
6759
+ projectRoot: process11.cwd(),
6426
6760
  server: options.server
6427
6761
  });
6428
6762
  if (options.open !== false) {
@@ -6432,17 +6766,17 @@ async function manageCommand(options) {
6432
6766
  log.info("Press Ctrl+C to stop.");
6433
6767
  await new Promise((resolveClose, rejectClose) => {
6434
6768
  const shutdown = () => {
6435
- process10.off("SIGINT", shutdown);
6436
- process10.off("SIGTERM", shutdown);
6769
+ process11.off("SIGINT", shutdown);
6770
+ process11.off("SIGTERM", shutdown);
6437
6771
  server.close().then(resolveClose).catch(rejectClose);
6438
6772
  };
6439
- process10.once("SIGINT", shutdown);
6440
- process10.once("SIGTERM", shutdown);
6773
+ process11.once("SIGINT", shutdown);
6774
+ process11.once("SIGTERM", shutdown);
6441
6775
  });
6442
6776
  } catch (error) {
6443
6777
  const message = error instanceof Error ? error.message : String(error);
6444
6778
  log.error(message);
6445
- process10.exit(1);
6779
+ process11.exit(1);
6446
6780
  }
6447
6781
  }
6448
6782
 
@@ -6450,7 +6784,7 @@ async function manageCommand(options) {
6450
6784
  init_esm_shims();
6451
6785
  import { rm as rm6 } from "fs/promises";
6452
6786
  import { join as join13, resolve as resolve7 } from "path";
6453
- import process11 from "process";
6787
+ import process12 from "process";
6454
6788
  init_tui();
6455
6789
  async function selectInstallScope(options) {
6456
6790
  if (options.global != null) {
@@ -6469,7 +6803,7 @@ async function selectInstallScope(options) {
6469
6803
  });
6470
6804
  if (isCancel(result)) {
6471
6805
  cancel("Install cancelled.");
6472
- process11.exit(0);
6806
+ process12.exit(0);
6473
6807
  }
6474
6808
  return result;
6475
6809
  }
@@ -6504,7 +6838,7 @@ async function selectInstallAgents(options, global) {
6504
6838
  });
6505
6839
  if (isCancel(selected)) {
6506
6840
  cancel("Install cancelled.");
6507
- process11.exit(0);
6841
+ process12.exit(0);
6508
6842
  }
6509
6843
  return selected;
6510
6844
  }
@@ -6521,7 +6855,7 @@ async function confirmHooksOptIn(hookCount, options) {
6521
6855
  });
6522
6856
  if (isCancel(result)) {
6523
6857
  cancel("Install cancelled.");
6524
- process11.exit(0);
6858
+ process12.exit(0);
6525
6859
  }
6526
6860
  return result;
6527
6861
  }
@@ -6546,7 +6880,7 @@ async function marketplaceLibraryCommand(options) {
6546
6880
  log.error(
6547
6881
  error instanceof Error ? error.message : "Failed to fetch library"
6548
6882
  );
6549
- process11.exit(1);
6883
+ process12.exit(1);
6550
6884
  }
6551
6885
  }
6552
6886
  async function resolveAndInstallPack(slug, options, overrides) {
@@ -6569,7 +6903,8 @@ async function resolveAndInstallPack(slug, options, overrides) {
6569
6903
  manifest,
6570
6904
  agents: agents2,
6571
6905
  global,
6572
- allowHooks: options.allowHooks
6906
+ allowHooks: options.allowHooks,
6907
+ serverUrl: options.server
6573
6908
  });
6574
6909
  }
6575
6910
  async function marketplaceInstallCommand(slug, options) {
@@ -6580,7 +6915,7 @@ async function marketplaceInstallCommand(slug, options) {
6580
6915
  log.error(
6581
6916
  error instanceof Error ? error.message : "Failed to install pack"
6582
6917
  );
6583
- process11.exit(1);
6918
+ process12.exit(1);
6584
6919
  }
6585
6920
  }
6586
6921
  function addSkillToPack(packMap, slug, versionId, skillName, agent, installPath) {
@@ -6634,7 +6969,7 @@ async function marketplaceUpdateCommand(slug, options) {
6634
6969
  const toUpdate = slug ? installed.filter((p) => p.slug === slug) : installed;
6635
6970
  if (toUpdate.length === 0) {
6636
6971
  log.error(`Pack "${slug}" is not installed.`);
6637
- process11.exit(1);
6972
+ process12.exit(1);
6638
6973
  }
6639
6974
  let updated = 0;
6640
6975
  for (const pack of toUpdate) {
@@ -6654,7 +6989,7 @@ async function marketplaceUpdateCommand(slug, options) {
6654
6989
  log.error(
6655
6990
  error instanceof Error ? error.message : "Failed to update packs"
6656
6991
  );
6657
- process11.exit(1);
6992
+ process12.exit(1);
6658
6993
  }
6659
6994
  }
6660
6995
  async function selectPacksToRemove(installed, slug, options) {
@@ -6680,7 +7015,7 @@ async function selectPacksToRemove(installed, slug, options) {
6680
7015
  });
6681
7016
  if (isCancel(selected)) {
6682
7017
  cancel("Remove cancelled.");
6683
- process11.exit(0);
7018
+ process12.exit(0);
6684
7019
  }
6685
7020
  return selected;
6686
7021
  }
@@ -6718,7 +7053,7 @@ async function marketplaceRemoveCommand(slug, options) {
6718
7053
  });
6719
7054
  if (isCancel(confirmed) || !confirmed) {
6720
7055
  cancel("Remove cancelled.");
6721
- process11.exit(0);
7056
+ process12.exit(0);
6722
7057
  }
6723
7058
  }
6724
7059
  for (const pack of toRemove) {
@@ -6729,7 +7064,7 @@ async function marketplaceRemoveCommand(slug, options) {
6729
7064
  log.error(
6730
7065
  error instanceof Error ? error.message : "Failed to remove packs"
6731
7066
  );
6732
- process11.exit(1);
7067
+ process12.exit(1);
6733
7068
  }
6734
7069
  }
6735
7070
 
@@ -6737,19 +7072,6 @@ async function marketplaceRemoveCommand(slug, options) {
6737
7072
  init_esm_shims();
6738
7073
  init_api();
6739
7074
  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
7075
  var parseContext = (contextJson) => {
6754
7076
  if (!contextJson) {
6755
7077
  return void 0;
@@ -6770,7 +7092,7 @@ var run2 = async (command, args, options) => {
6770
7092
  apiOptions
6771
7093
  );
6772
7094
  if (options.json) {
6773
- writeJson4(result);
7095
+ writeJson(result);
6774
7096
  return;
6775
7097
  }
6776
7098
  log.success(`profiles ${command} completed`);
@@ -6779,34 +7101,34 @@ async function profilesListCommand(options) {
6779
7101
  try {
6780
7102
  await run2("list", {}, options);
6781
7103
  } catch (error) {
6782
- exitWithError3(error);
7104
+ exitCommandError(error, options);
6783
7105
  }
6784
7106
  }
6785
7107
  async function profilesGetCommand(options) {
6786
7108
  try {
6787
7109
  if (!(options.id || options.name)) {
6788
- fail2("profiles get requires --id or --name");
7110
+ fail("profiles get requires --id or --name");
6789
7111
  }
6790
7112
  await run2("get", { id: options.id, name: options.name }, options);
6791
7113
  } catch (error) {
6792
- exitWithError3(error);
7114
+ exitCommandError(error, options);
6793
7115
  }
6794
7116
  }
6795
7117
  async function profilesCreateCommand(options) {
6796
7118
  try {
6797
- const name = options.name ?? fail2("profiles create requires --name");
7119
+ const name = options.name ?? fail("profiles create requires --name");
6798
7120
  await run2(
6799
7121
  "create",
6800
7122
  { name, context: parseContext(options.contextJson) },
6801
7123
  options
6802
7124
  );
6803
7125
  } catch (error) {
6804
- exitWithError3(error);
7126
+ exitCommandError(error, options);
6805
7127
  }
6806
7128
  }
6807
7129
  async function profilesUpdateCommand(options) {
6808
7130
  try {
6809
- const id = options.id ?? fail2("profiles update requires --id");
7131
+ const id = options.id ?? fail("profiles update requires --id");
6810
7132
  await run2(
6811
7133
  "update",
6812
7134
  {
@@ -6817,26 +7139,26 @@ async function profilesUpdateCommand(options) {
6817
7139
  options
6818
7140
  );
6819
7141
  } catch (error) {
6820
- exitWithError3(error);
7142
+ exitCommandError(error, options);
6821
7143
  }
6822
7144
  }
6823
7145
  async function profilesRemoveCommand(options) {
6824
7146
  try {
6825
- const id = options.id ?? fail2("profiles remove requires --id");
7147
+ const id = options.id ?? fail("profiles remove requires --id");
6826
7148
  if (!options.yes) {
6827
- fail2("profiles remove requires --yes");
7149
+ fail("profiles remove requires --yes");
6828
7150
  }
6829
7151
  await run2("remove", { id, yes: true }, options);
6830
7152
  } catch (error) {
6831
- exitWithError3(error);
7153
+ exitCommandError(error, options);
6832
7154
  }
6833
7155
  }
6834
7156
  async function profilesSetDefaultCommand(options) {
6835
7157
  try {
6836
- const id = options.id ?? fail2("profiles set-default requires --id");
7158
+ const id = options.id ?? fail("profiles set-default requires --id");
6837
7159
  await run2("set-default", { id }, options);
6838
7160
  } catch (error) {
6839
- exitWithError3(error);
7161
+ exitCommandError(error, options);
6840
7162
  }
6841
7163
  }
6842
7164
 
@@ -6844,14 +7166,6 @@ async function profilesSetDefaultCommand(options) {
6844
7166
  init_esm_shims();
6845
7167
  init_api();
6846
7168
  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
7169
  var run3 = async (command, args, options) => {
6856
7170
  const apiOptions = {
6857
7171
  ...options.server ? { serverUrl: options.server } : {},
@@ -6866,39 +7180,34 @@ var run3 = async (command, args, options) => {
6866
7180
  apiOptions
6867
7181
  );
6868
7182
  if (options.json) {
6869
- writeJson5(result);
7183
+ writeJson(result);
6870
7184
  return;
6871
7185
  }
6872
7186
  log.success(`projects ${command} completed`);
6873
7187
  };
6874
- var exitWithError4 = (error) => {
6875
- const message = error instanceof Error ? error.message : String(error);
6876
- log.error(message);
6877
- process13.exit(1);
6878
- };
6879
7188
  async function projectsGetCommand(options) {
6880
7189
  try {
6881
- const id = options.id ?? fail3("projects get requires --id");
7190
+ const id = options.id ?? fail("projects get requires --id");
6882
7191
  await run3("get", { id }, options);
6883
7192
  } catch (error) {
6884
- exitWithError4(error);
7193
+ exitCommandError(error, options);
6885
7194
  }
6886
7195
  }
6887
7196
  async function projectsCreateCommand(options) {
6888
7197
  try {
6889
- const name = options.name ?? fail3("projects create requires --name");
7198
+ const name = options.name ?? fail("projects create requires --name");
6890
7199
  await run3(
6891
7200
  "create",
6892
7201
  { name, description: options.description, orgId: options.orgId },
6893
7202
  options
6894
7203
  );
6895
7204
  } catch (error) {
6896
- exitWithError4(error);
7205
+ exitCommandError(error, options);
6897
7206
  }
6898
7207
  }
6899
7208
  async function projectsUpdateCommand(options) {
6900
7209
  try {
6901
- const id = options.id ?? fail3("projects update requires --id");
7210
+ const id = options.id ?? fail("projects update requires --id");
6902
7211
  await run3(
6903
7212
  "update",
6904
7213
  {
@@ -6909,18 +7218,18 @@ async function projectsUpdateCommand(options) {
6909
7218
  options
6910
7219
  );
6911
7220
  } catch (error) {
6912
- exitWithError4(error);
7221
+ exitCommandError(error, options);
6913
7222
  }
6914
7223
  }
6915
7224
  async function projectsRemoveCommand(options) {
6916
7225
  try {
6917
- const id = options.id ?? fail3("projects remove requires --id");
7226
+ const id = options.id ?? fail("projects remove requires --id");
6918
7227
  if (!options.yes) {
6919
- fail3("projects remove requires --yes");
7228
+ fail("projects remove requires --yes");
6920
7229
  }
6921
7230
  await run3("remove", { id, yes: true }, options);
6922
7231
  } catch (error) {
6923
- exitWithError4(error);
7232
+ exitCommandError(error, options);
6924
7233
  }
6925
7234
  }
6926
7235
 
@@ -6928,11 +7237,6 @@ async function projectsRemoveCommand(options) {
6928
7237
  init_esm_shims();
6929
7238
  init_api();
6930
7239
  init_tui();
6931
- import process14 from "process";
6932
- var writeJson6 = (value) => {
6933
- process14.stdout.write(`${JSON.stringify(value, null, 2)}
6934
- `);
6935
- };
6936
7240
  var parseCsv3 = (input) => {
6937
7241
  if (!input) {
6938
7242
  return void 0;
@@ -6940,14 +7244,6 @@ var parseCsv3 = (input) => {
6940
7244
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
6941
7245
  return values.length > 0 ? values : void 0;
6942
7246
  };
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
7247
  var run4 = async (command, args, options) => {
6952
7248
  const apiOptions = {
6953
7249
  ...options.server ? { serverUrl: options.server } : {},
@@ -6962,69 +7258,69 @@ var run4 = async (command, args, options) => {
6962
7258
  apiOptions
6963
7259
  );
6964
7260
  if (options.json) {
6965
- writeJson6(result);
7261
+ writeJson(result);
6966
7262
  return;
6967
7263
  }
6968
7264
  log.success(`references ${command} completed`);
6969
7265
  };
6970
7266
  async function referencesListCommand(options) {
6971
7267
  try {
6972
- const ruleId = options.ruleId ?? fail4("references list requires --rule-id");
7268
+ const ruleId = options.ruleId ?? fail("references list requires --rule-id");
6973
7269
  await run4("list", { ruleId }, options);
6974
7270
  } catch (error) {
6975
- exitWithError5(error);
7271
+ exitCommandError(error, options);
6976
7272
  }
6977
7273
  }
6978
7274
  async function referencesGetCommand(options) {
6979
7275
  try {
6980
- const id = options.id ?? fail4("references get requires --id");
7276
+ const id = options.id ?? fail("references get requires --id");
6981
7277
  await run4("get", { id }, options);
6982
7278
  } catch (error) {
6983
- exitWithError5(error);
7279
+ exitCommandError(error, options);
6984
7280
  }
6985
7281
  }
6986
7282
  async function referencesCreateCommand(options) {
6987
7283
  try {
6988
- const ruleId = options.ruleId ?? fail4("references create requires --rule-id");
6989
- const file = options.file ?? fail4("references create requires --file");
7284
+ const ruleId = options.ruleId ?? fail("references create requires --rule-id");
7285
+ const file = options.file ?? fail("references create requires --file");
6990
7286
  await run4("create", { ruleId, file }, options);
6991
7287
  } catch (error) {
6992
- exitWithError5(error);
7288
+ exitCommandError(error, options);
6993
7289
  }
6994
7290
  }
6995
7291
  async function referencesUpdateCommand(options) {
6996
7292
  try {
6997
- const id = options.id ?? fail4("references update requires --id");
7293
+ const id = options.id ?? fail("references update requires --id");
6998
7294
  await run4(
6999
7295
  "update",
7000
7296
  { id, label: options.label, replaceFile: options.replaceFile },
7001
7297
  options
7002
7298
  );
7003
7299
  } catch (error) {
7004
- exitWithError5(error);
7300
+ exitCommandError(error, options);
7005
7301
  }
7006
7302
  }
7007
7303
  async function referencesRemoveCommand(options) {
7008
7304
  try {
7009
- const id = options.id ?? fail4("references remove requires --id");
7305
+ const id = options.id ?? fail("references remove requires --id");
7010
7306
  if (!options.yes) {
7011
- fail4("references remove requires --yes");
7307
+ fail("references remove requires --yes");
7012
7308
  }
7013
7309
  await run4("remove", { id, yes: true }, options);
7014
7310
  } catch (error) {
7015
- exitWithError5(error);
7311
+ exitCommandError(error, options);
7016
7312
  }
7017
7313
  }
7018
7314
  async function referencesReorderCommand(options) {
7019
7315
  try {
7020
- const ruleId = options.ruleId ?? fail4("references reorder requires --rule-id");
7316
+ const ruleId = options.ruleId ?? fail("references reorder requires --rule-id");
7021
7317
  const orderedIds = parseCsv3(options.orderedIds);
7022
7318
  if (!orderedIds || orderedIds.length === 0) {
7023
- fail4("references reorder requires --ordered-ids");
7319
+ fail("references reorder requires --ordered-ids");
7024
7320
  }
7025
7321
  await run4("reorder", { ruleId, orderedIds }, options);
7026
7322
  } catch (error) {
7027
- exitWithError5(error);
7323
+ exitCommandError(error, options);
7028
7324
  }
7029
7325
  }
7030
7326
 
@@ -7032,7 +7328,7 @@ async function referencesReorderCommand(options) {
7032
7328
  init_esm_shims();
7033
7329
  import { rm as rm7 } from "fs/promises";
7034
7330
  import { join as join14, resolve as resolve8 } from "path";
7035
- import process15 from "process";
7331
+ import process13 from "process";
7036
7332
  init_tui();
7037
7333
  async function collectInstalledSkills(detectedAgents, options) {
7038
7334
  const skillsToRemove = [];
@@ -7065,7 +7361,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7065
7361
  if (selected.length === 0) {
7066
7362
  log.error(`Skill '${options.skill}' not found.`);
7067
7363
  log.info("Run 'braid list' to see installed skills.");
7068
- process15.exit(1);
7364
+ process13.exit(1);
7069
7365
  }
7070
7366
  return selected;
7071
7367
  }
@@ -7084,7 +7380,7 @@ async function selectSkillsToRemove(skillsToRemove, options) {
7084
7380
  });
7085
7381
  if (isCancel(result)) {
7086
7382
  cancel("Remove cancelled.");
7087
- process15.exit(0);
7383
+ process13.exit(0);
7088
7384
  }
7089
7385
  return result;
7090
7386
  }
@@ -7098,7 +7394,7 @@ async function confirmRemoval(selectedCount, options) {
7098
7394
  });
7099
7395
  if (isCancel(confirmed) || !confirmed) {
7100
7396
  cancel("Remove cancelled.");
7101
- process15.exit(0);
7397
+ process13.exit(0);
7102
7398
  }
7103
7399
  }
7104
7400
  async function removeSkill(skill, removeSpinner) {
@@ -7162,7 +7458,7 @@ async function removeCommand(options) {
7162
7458
  removeSpinner.stop("Remove failed");
7163
7459
  const message = error instanceof Error ? error.message : String(error);
7164
7460
  log.error(message);
7165
- process15.exit(1);
7461
+ process13.exit(1);
7166
7462
  }
7167
7463
  }
7168
7464
 
@@ -7221,6 +7517,7 @@ async function retractCommand(source, _options) {
7221
7517
  // src/commands/rollback.ts
7222
7518
  init_esm_shims();
7223
7519
  init_api();
7520
+ init_config();
7224
7521
  init_lockfile();
7225
7522
  init_tui();
7226
7523
  var PUBLIC_SOURCE_REGEX2 = /^@([a-z0-9-]+)\/([a-z0-9-]+)$/;
@@ -7239,7 +7536,8 @@ async function findPreviousActiveVersion(args) {
7239
7536
  args.slug,
7240
7537
  args.ruleIds,
7241
7538
  args.server,
7242
- v
7539
+ v,
7540
+ args.apiKey
7243
7541
  );
7244
7542
  return { version: v, response };
7245
7543
  } catch (error) {
@@ -7253,6 +7551,8 @@ async function findPreviousActiveVersion(args) {
7253
7551
  return null;
7254
7552
  }
7255
7553
  async function rollbackCommand(source, options) {
7554
+ const config = await loadMergedConfigAsync();
7555
+ const serverUrl = options.server ?? config.serverUrl;
7256
7556
  intro(`Rolling back ${source}`);
7257
7557
  const rollbackSpinner = spinner();
7258
7558
  rollbackSpinner.start("Reading lockfile...");
@@ -7317,7 +7617,8 @@ async function rollbackCommand(source, options) {
7317
7617
  slug: publicSource.slug,
7318
7618
  currentVersion: parsed.version,
7319
7619
  ruleIds: currentValue.ruleIds,
7320
- server: options.server
7620
+ server: serverUrl,
7621
+ apiKey: config.token
7321
7622
  });
7322
7623
  if (!result) {
7323
7624
  rollbackSpinner.stop("No active version found");
@@ -7360,7 +7661,6 @@ async function rollbackCommand(source, options) {
7360
7661
  init_esm_shims();
7361
7662
  init_api();
7362
7663
  init_tui();
7363
- import process16 from "process";
7364
7664
  var parseCsv4 = (input) => {
7365
7665
  if (!input) {
7366
7666
  return void 0;
@@ -7368,18 +7668,6 @@ var parseCsv4 = (input) => {
7368
7668
  const values = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
7369
7669
  return values.length > 0 ? values : void 0;
7370
7670
  };
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
7671
  var run5 = async (command, args, options) => {
7384
7672
  const apiOptions = {
7385
7673
  ...options.server ? { serverUrl: options.server } : {},
@@ -7394,23 +7682,23 @@ var run5 = async (command, args, options) => {
7394
7682
  apiOptions
7395
7683
  );
7396
7684
  if (options.json) {
7397
- writeJson7(result);
7685
+ writeJson(result);
7398
7686
  return;
7399
7687
  }
7400
7688
  log.success(`rules ${command} completed`);
7401
7689
  };
7402
7690
  async function rulesGetCommand(options) {
7403
7691
  try {
7404
- const id = options.id ?? fail5("rules get requires --id");
7692
+ const id = options.id ?? fail("rules get requires --id");
7405
7693
  await run5("get", { id }, options);
7406
7694
  } catch (error) {
7407
- exitWithError6(error);
7695
+ exitCommandError(error, options);
7408
7696
  }
7409
7697
  }
7410
7698
  async function rulesCreateCommand(options) {
7411
7699
  try {
7412
- const title = options.title ?? fail5("rules create requires --title");
7413
- const content = options.content ?? fail5("rules create requires --content");
7700
+ const title = options.title ?? fail("rules create requires --title");
7701
+ const content = options.content ?? fail("rules create requires --content");
7414
7702
  await run5(
7415
7703
  "create",
7416
7704
  {
@@ -7423,12 +7711,12 @@ async function rulesCreateCommand(options) {
7423
7711
  options
7424
7712
  );
7425
7713
  } catch (error) {
7426
- exitWithError6(error);
7714
+ exitCommandError(error, options);
7427
7715
  }
7428
7716
  }
7429
7717
  async function rulesUpdateCommand(options) {
7430
7718
  try {
7431
- const id = options.id ?? fail5("rules update requires --id");
7719
+ const id = options.id ?? fail("rules update requires --id");
7432
7720
  await run5(
7433
7721
  "update",
7434
7722
  {
@@ -7441,120 +7729,120 @@ async function rulesUpdateCommand(options) {
7441
7729
  options
7442
7730
  );
7443
7731
  } catch (error) {
7444
- exitWithError6(error);
7732
+ exitCommandError(error, options);
7445
7733
  }
7446
7734
  }
7447
7735
  async function rulesRemoveCommand(options) {
7448
7736
  try {
7449
- const id = options.id ?? fail5("rules remove requires --id");
7737
+ const id = options.id ?? fail("rules remove requires --id");
7450
7738
  if (!options.yes) {
7451
- fail5("rules remove requires --yes");
7739
+ fail("rules remove requires --yes");
7452
7740
  }
7453
7741
  await run5("remove", { id, yes: true }, options);
7454
7742
  } catch (error) {
7455
- exitWithError6(error);
7743
+ exitCommandError(error, options);
7456
7744
  }
7457
7745
  }
7458
7746
  async function rulesEnableCommand(options) {
7459
7747
  try {
7460
- const id = options.id ?? fail5("rules enable requires --id");
7748
+ const id = options.id ?? fail("rules enable requires --id");
7461
7749
  await run5("enable", { id }, options);
7462
7750
  } catch (error) {
7463
- exitWithError6(error);
7751
+ exitCommandError(error, options);
7464
7752
  }
7465
7753
  }
7466
7754
  async function rulesDisableCommand(options) {
7467
7755
  try {
7468
- const id = options.id ?? fail5("rules disable requires --id");
7756
+ const id = options.id ?? fail("rules disable requires --id");
7469
7757
  await run5("disable", { id }, options);
7470
7758
  } catch (error) {
7471
- exitWithError6(error);
7759
+ exitCommandError(error, options);
7472
7760
  }
7473
7761
  }
7474
7762
  async function rulesMoveCommand(options) {
7475
7763
  try {
7476
- const id = options.id ?? fail5("rules move requires --id");
7477
- const projectId = options.projectId ?? fail5("rules move requires --project-id");
7764
+ const id = options.id ?? fail("rules move requires --id");
7765
+ const projectId = options.projectId ?? fail("rules move requires --project-id");
7478
7766
  await run5("move", { id, projectId }, options);
7479
7767
  } catch (error) {
7480
- exitWithError6(error);
7768
+ exitCommandError(error, options);
7481
7769
  }
7482
7770
  }
7483
7771
  async function rulesDuplicateCommand(options) {
7484
7772
  try {
7485
- const id = options.id ?? fail5("rules duplicate requires --id");
7773
+ const id = options.id ?? fail("rules duplicate requires --id");
7486
7774
  await run5(
7487
7775
  "duplicate",
7488
7776
  { id, targetProjectId: options.targetProjectId },
7489
7777
  options
7490
7778
  );
7491
7779
  } catch (error) {
7492
- exitWithError6(error);
7780
+ exitCommandError(error, options);
7493
7781
  }
7494
7782
  }
7495
7783
  async function rulesForkCommand(options) {
7496
7784
  try {
7497
- const id = options.id ?? fail5("rules fork requires --id");
7785
+ const id = options.id ?? fail("rules fork requires --id");
7498
7786
  await run5(
7499
7787
  "fork",
7500
7788
  { id, targetProjectId: options.targetProjectId },
7501
7789
  options
7502
7790
  );
7503
7791
  } catch (error) {
7504
- exitWithError6(error);
7792
+ exitCommandError(error, options);
7505
7793
  }
7506
7794
  }
7507
7795
  async function rulesSyncStatusCommand(options) {
7508
7796
  try {
7509
7797
  await run5("sync-status", { id: options.id }, options);
7510
7798
  } catch (error) {
7511
- exitWithError6(error);
7799
+ exitCommandError(error, options);
7512
7800
  }
7513
7801
  }
7514
7802
  async function rulesSyncHistoryCommand(options) {
7515
7803
  try {
7516
- const id = options.id ?? fail5("rules sync-history requires --id");
7804
+ const id = options.id ?? fail("rules sync-history requires --id");
7517
7805
  await run5("sync-history", { id }, options);
7518
7806
  } catch (error) {
7519
- exitWithError6(error);
7807
+ exitCommandError(error, options);
7520
7808
  }
7521
7809
  }
7522
7810
  async function rulesSyncEnableCommand(options) {
7523
7811
  try {
7524
- const id = options.id ?? fail5("rules sync-enable requires --id");
7812
+ const id = options.id ?? fail("rules sync-enable requires --id");
7525
7813
  await run5("sync-enable", { id }, options);
7526
7814
  } catch (error) {
7527
- exitWithError6(error);
7815
+ exitCommandError(error, options);
7528
7816
  }
7529
7817
  }
7530
7818
  async function rulesSyncDisableCommand(options) {
7531
7819
  try {
7532
- const id = options.id ?? fail5("rules sync-disable requires --id");
7820
+ const id = options.id ?? fail("rules sync-disable requires --id");
7533
7821
  await run5("sync-disable", { id }, options);
7534
7822
  } catch (error) {
7535
- exitWithError6(error);
7823
+ exitCommandError(error, options);
7536
7824
  }
7537
7825
  }
7538
7826
  async function rulesSyncCheckCommand(options) {
7539
7827
  try {
7540
- const id = options.id ?? fail5("rules sync-check requires --id");
7828
+ const id = options.id ?? fail("rules sync-check requires --id");
7541
7829
  await run5("sync-check", { id }, options);
7542
7830
  } catch (error) {
7543
- exitWithError6(error);
7831
+ exitCommandError(error, options);
7544
7832
  }
7545
7833
  }
7546
7834
  async function rulesSyncNowCommand(options) {
7547
7835
  try {
7548
- const id = options.id ?? fail5("rules sync-now requires --id");
7836
+ const id = options.id ?? fail("rules sync-now requires --id");
7549
7837
  await run5("sync-now", { id }, options);
7550
7838
  } catch (error) {
7551
- exitWithError6(error);
7839
+ exitCommandError(error, options);
7552
7840
  }
7553
7841
  }
7554
7842
 
7555
7843
  // src/commands/scaffold.ts
7556
7844
  init_esm_shims();
7557
- import process17 from "process";
7845
+ import process14 from "process";
7558
7846
 
7559
7847
  // src/lib/scaffold.ts
7560
7848
  init_esm_shims();
@@ -8514,11 +8802,11 @@ var normalizeReferenceList = (referenceLabel, availableNamesLabel, values, avail
8514
8802
  };
8515
8803
  var exitCancelled2 = () => {
8516
8804
  cancel("Scaffold cancelled.");
8517
- process17.exit(0);
8805
+ process14.exit(0);
8518
8806
  };
8519
- var exitWithError7 = (message) => {
8807
+ var exitWithError = (message) => {
8520
8808
  log.error(message);
8521
- process17.exit(1);
8809
+ process14.exit(1);
8522
8810
  };
8523
8811
  var requirePromptValue = (value) => {
8524
8812
  if (isCancel(value)) {
@@ -8531,7 +8819,7 @@ var resolveType = async (options) => {
8531
8819
  return options.type;
8532
8820
  }
8533
8821
  if (options.yes) {
8534
- return exitWithError7("--type is required when using --yes");
8822
+ return exitWithError("--type is required when using --yes");
8535
8823
  }
8536
8824
  const selected = requirePromptValue(
8537
8825
  await select({
@@ -8561,7 +8849,7 @@ var resolveName = async (options) => {
8561
8849
  return options.name.trim();
8562
8850
  }
8563
8851
  if (options.yes) {
8564
- return exitWithError7("--name is required when using --yes");
8852
+ return exitWithError("--name is required when using --yes");
8565
8853
  }
8566
8854
  const value = requirePromptValue(
8567
8855
  await text({
@@ -8647,7 +8935,7 @@ var resolveOptionalReference = async (label, flagValue, options, availableNames)
8647
8935
  return selected || void 0;
8648
8936
  };
8649
8937
  var buildScaffoldInput = async (options) => {
8650
- const context = await inspectScaffoldDirectory(process17.cwd());
8938
+ const context = await inspectScaffoldDirectory(process14.cwd());
8651
8939
  const availableSkillNames = context.manifest.skills.map(
8652
8940
  (entry) => entry.name
8653
8941
  );
@@ -8701,7 +8989,7 @@ var buildScaffoldInput = async (options) => {
8701
8989
  availableWorkflowNames
8702
8990
  ) : void 0;
8703
8991
  return {
8704
- cwd: process17.cwd(),
8992
+ cwd: process14.cwd(),
8705
8993
  type,
8706
8994
  name,
8707
8995
  title,
@@ -8730,10 +9018,10 @@ var finishScaffoldSuccess = (type, name, createdFiles) => {
8730
9018
  var isMockedProcessExitError = (error) => error instanceof Error && error.message.startsWith("process.exit:");
8731
9019
  var handleScaffoldCommandFailure = (error) => {
8732
9020
  if (error instanceof ScaffoldError) {
8733
- exitWithError7(error.message);
9021
+ exitWithError(error.message);
8734
9022
  }
8735
9023
  if (error instanceof Error && !isMockedProcessExitError(error)) {
8736
- exitWithError7(error.message);
9024
+ exitWithError(error.message);
8737
9025
  }
8738
9026
  throw error;
8739
9027
  };