routstrd 0.3.0 → 0.3.1

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/src/cli.ts CHANGED
@@ -3,6 +3,7 @@ import { startDaemon } from "./start-daemon";
3
3
  import {
4
4
  handleDaemonCommand,
5
5
  callDaemon,
6
+ callAuth,
6
7
  ensureDaemonRunning,
7
8
  isDaemonRunning,
8
9
  loadConfig,
@@ -81,7 +82,7 @@ async function printLightningInvoice(invoice: string): Promise<void> {
81
82
  }
82
83
 
83
84
  async function installCocodOrExit(): Promise<void> {
84
- logger.log("cocod not found. Installing globally with bun...");
85
+ console.log("cocod not found. Installing globally with bun...");
85
86
 
86
87
  const installProc = Bun.spawn(
87
88
  ["bun", "install", "--global", "@routstr/cocod"],
@@ -93,13 +94,13 @@ async function installCocodOrExit(): Promise<void> {
93
94
 
94
95
  const installCode = await installProc.exited;
95
96
  if (installCode !== 0 || !(await isCocodInstalled())) {
96
- logger.error(
97
+ console.error(
97
98
  "Failed to install cocod. Please run 'bun install --global @routstr/cocod' manually.",
98
99
  );
99
100
  throw new Error("cocod installation failed");
100
101
  }
101
102
 
102
- logger.log("cocod installed successfully.");
103
+ console.log("cocod installed successfully.");
103
104
  }
104
105
 
105
106
  async function requireLocalDaemon(): Promise<void> {
@@ -113,12 +114,12 @@ async function requireLocalDaemon(): Promise<void> {
113
114
  }
114
115
 
115
116
  async function initDaemon(): Promise<void> {
116
- logger.log("Initializing routstrd...");
117
+ console.log("Initializing routstrd...");
117
118
 
118
119
  // Create config directory
119
120
  if (!existsSync(CONFIG_DIR)) {
120
121
  mkdirSync(CONFIG_DIR, { recursive: true });
121
- logger.log(`Created config directory: ${CONFIG_DIR}`);
122
+ console.log(`Created config directory: ${CONFIG_DIR}`);
122
123
  }
123
124
 
124
125
  // Create initial config
@@ -128,7 +129,7 @@ async function initDaemon(): Promise<void> {
128
129
  cocodPath: null,
129
130
  };
130
131
  await Bun.write(CONFIG_FILE, JSON.stringify(config, null, 2));
131
- logger.log(`Created config file: ${CONFIG_FILE}`);
132
+ console.log(`Created config file: ${CONFIG_FILE}`);
132
133
  }
133
134
 
134
135
  const config = await loadConfig();
@@ -190,30 +191,30 @@ async function initDaemon(): Promise<void> {
190
191
  const alreadyInitialized = combinedOutput.includes("already initialized");
191
192
 
192
193
  if (initCode !== 0 && !alreadyInitialized) {
193
- logger.error(
194
+ console.error(
194
195
  "Failed to initialize cocod. Please run 'cocod init' manually.",
195
196
  );
196
197
  return;
197
198
  }
198
199
 
199
200
  if (alreadyInitialized) {
200
- logger.log("cocod is already initialized.");
201
+ console.log("cocod is already initialized.");
201
202
  } else {
202
- logger.log("cocod initialized successfully.");
203
+ console.log("cocod initialized successfully.");
203
204
  }
204
205
 
205
206
  await startDaemon({ port: String(config.port || 8008) });
206
207
 
207
208
  await setupIntegration(config);
208
209
 
209
- logger.log("\nInitialization complete!");
210
- logger.log(
210
+ console.log("\nInitialization complete!");
211
+ console.log(
211
212
  "\n use 'routstrd receive <cashu-token>' or 'routstrd receive 2100' to top up your local wallet using Lightning!",
212
213
  );
213
- logger.log(
214
+ console.log(
214
215
  "\n full wallet commands still work too, e.g. 'routstrd wallet receive cashu <token>' and 'routstrd wallet receive bolt11 2100'.",
215
216
  );
216
- logger.log(
217
+ console.log(
217
218
  "\nTo ensure routstrd persists across system restarts, run: 'routstrd service install'",
218
219
  );
219
220
  }
@@ -334,7 +335,8 @@ program
334
335
  program
335
336
  .command("remote <url>")
336
337
  .description("Configure a remote daemon URL")
337
- .action(async (url: string) => {
338
+ .option("--auth-url <authUrl>", "URL of the auth proxy for management commands (npubs, clients, usage)")
339
+ .action(async (url: string, options: { authUrl?: string }) => {
338
340
  try {
339
341
  new URL(url);
340
342
  } catch {
@@ -342,12 +344,24 @@ program
342
344
  process.exit(1);
343
345
  }
344
346
 
347
+ if (options.authUrl) {
348
+ try {
349
+ new URL(options.authUrl);
350
+ } catch {
351
+ console.error(`Invalid auth URL: ${options.authUrl}`);
352
+ process.exit(1);
353
+ }
354
+ }
355
+
345
356
  if (!existsSync(CONFIG_DIR)) {
346
357
  mkdirSync(CONFIG_DIR, { recursive: true });
347
358
  }
348
359
 
349
360
  const config = await loadConfig();
350
361
  const updates: Partial<RoutstrdConfig> = { daemonUrl: url };
362
+ if (options.authUrl) {
363
+ updates.authUrl = options.authUrl;
364
+ }
351
365
  let generatedNpub: string | undefined;
352
366
 
353
367
  if (!config.nsec) {
@@ -366,6 +380,9 @@ program
366
380
  await Bun.write(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
367
381
 
368
382
  console.log(`Remote daemon URL set to: ${url}`);
383
+ if (options.authUrl) {
384
+ console.log(`Auth proxy URL set to: ${options.authUrl}`);
385
+ }
369
386
  if (generatedNpub) {
370
387
  console.log(
371
388
  `\nA new Nostr identity has been generated for remote authentication.`,
@@ -666,7 +683,7 @@ program
666
683
  ? Math.min(requested, 1000)
667
684
  : 10;
668
685
 
669
- const result = await callDaemon(`/usage?limit=${limit}`);
686
+ const result = await callAuth(`/usage?limit=${limit}`);
670
687
  if (result.error) {
671
688
  console.log(result.error);
672
689
  process.exit(1);
@@ -882,7 +899,7 @@ npubsCmd
882
899
  await ensureDaemonRunning();
883
900
  const config = await loadConfig();
884
901
  const userNpub = getUserNpub(config);
885
- const result = await callDaemon("/npubs");
902
+ const result = await callAuth("/npubs");
886
903
  if (result.error) {
887
904
  console.log(result.error);
888
905
  process.exit(1);
@@ -925,7 +942,7 @@ npubsCmd
925
942
  );
926
943
  process.exit(1);
927
944
  }
928
- const result = await callDaemon("/npubs");
945
+ const result = await callAuth("/npubs");
929
946
  if (result.error) {
930
947
  console.log(result.error);
931
948
  process.exit(1);
@@ -943,7 +960,7 @@ npubsCmd
943
960
  console.error("Failed to normalize user npub.");
944
961
  process.exit(1);
945
962
  }
946
- const addResult = await callDaemon("/npubs", {
963
+ const addResult = await callAuth("/npubs", {
947
964
  method: "POST",
948
965
  body: { npub: npubFromPubkey(normalized) },
949
966
  });
@@ -977,7 +994,7 @@ npubsCmd
977
994
  process.exit(1);
978
995
  }
979
996
  const body: Record<string, string> = { npub: npubFromPubkey(normalized), role: options.role };
980
- const result = await callDaemon("/npubs", {
997
+ const result = await callAuth("/npubs", {
981
998
  method: "POST",
982
999
  body,
983
1000
  });
@@ -1010,7 +1027,7 @@ npubsCmd
1010
1027
  console.error("Invalid role. Expected 'admin' or 'user'.");
1011
1028
  process.exit(1);
1012
1029
  }
1013
- const result = await callDaemon("/npubs", {
1030
+ const result = await callAuth("/npubs", {
1014
1031
  method: "PATCH",
1015
1032
  body: { npub: npubFromPubkey(normalized), role: options.role },
1016
1033
  });
@@ -1039,7 +1056,7 @@ npubsCmd
1039
1056
  console.error("Invalid npub value. Use npub1... or 64-char hex pubkey.");
1040
1057
  process.exit(1);
1041
1058
  }
1042
- const result = await callDaemon(
1059
+ const result = await callAuth(
1043
1060
  `/npubs/${encodeURIComponent(npubFromPubkey(normalized))}`,
1044
1061
  {
1045
1062
  method: "DELETE",
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { readFile, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
- import { logger } from "../utils/logger";
6
5
  import type { IntegrationConfig, RoutstrModel } from "./registry";
7
6
  import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
8
7
 
@@ -13,8 +12,8 @@ export async function installClaudeCodeIntegration(
13
12
  ): Promise<void> {
14
13
  const { name, configPath } = integrationConfig;
15
14
 
16
- logger.log(`\nInstalling routstr configuration in ${configPath}...`);
17
- logger.log(`Using API key for ${name}`);
15
+ console.log(`\nInstalling routstr configuration in ${configPath}...`);
16
+ console.log(`Using API key for ${name}`);
18
17
 
19
18
  const baseUrl = getDaemonBaseUrl(config);
20
19
 
@@ -28,7 +27,7 @@ export async function installClaudeCodeIntegration(
28
27
  settings = JSON.parse(content);
29
28
  }
30
29
  } catch (error) {
31
- logger.error(`Error reading ${configPath}, creating new one.`);
30
+ console.error(`Error reading ${configPath}, creating new one.`);
32
31
  }
33
32
 
34
33
  if (!settings.env) {
@@ -49,25 +48,25 @@ export async function installClaudeCodeIntegration(
49
48
  settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = opus.id;
50
49
  settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = sonnet.id;
51
50
  settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = haiku.id;
52
- logger.log(`Set Claude models: Opus=${opus.id}, Sonnet=${sonnet.id}, Haiku=${haiku.id}`);
51
+ console.log(`Set Claude models: Opus=${opus.id}, Sonnet=${sonnet.id}, Haiku=${haiku.id}`);
53
52
  } else if (models.length > 0) {
54
53
  const model = models[0]!;
55
- logger.log(`Only ${models.length} models available, falling back to defaults.`);
54
+ console.log(`Only ${models.length} models available, falling back to defaults.`);
56
55
  settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = model.id;
57
56
  settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = model.id;
58
57
  settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = model.id;
59
58
  } else {
60
- logger.log("No models available from routstr daemon.");
59
+ console.log("No models available from routstr daemon.");
61
60
  }
62
61
  } catch (error) {
63
- logger.error("Failed to fetch models for Claude Code integration:", error);
62
+ console.error("Failed to fetch models for Claude Code integration:", error);
64
63
  }
65
64
 
66
65
  try {
67
66
  mkdirSync(dirname(configPath), { recursive: true });
68
67
  await writeFile(configPath, JSON.stringify(settings, null, 2));
69
- logger.log(`Successfully updated ${configPath} with routstr settings.`);
68
+ console.log(`Successfully updated ${configPath} with routstr settings.`);
70
69
  } catch (error) {
71
- logger.error(`Failed to write to ${configPath}:`, error);
70
+ console.error(`Failed to write to ${configPath}:`, error);
72
71
  }
73
72
  }
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { readFile, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
- import { logger } from "../utils/logger";
6
5
  import type { IntegrationConfig, RoutstrModel } from "./registry";
7
6
  import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
8
7
 
@@ -13,8 +12,8 @@ export async function installHermesIntegration(
13
12
  ): Promise<void> {
14
13
  const { name, configPath } = integrationConfig;
15
14
 
16
- logger.log(`\nInstalling routstr configuration in ${configPath}...`);
17
- logger.log(`Using API key for ${name}`);
15
+ console.log(`\nInstalling routstr configuration in ${configPath}...`);
16
+ console.log(`Using API key for ${name}`);
18
17
 
19
18
  const baseUrl = getDaemonBaseUrl(config);
20
19
  const baseUrlV1 = `${baseUrl}/v1`;
@@ -27,16 +26,16 @@ export async function installHermesIntegration(
27
26
 
28
27
  if (models.length >= 3) {
29
28
  defaultModel = models[2]!.id;
30
- logger.log(`Set default model to 3rd available model: ${defaultModel}`);
29
+ console.log(`Set default model to 3rd available model: ${defaultModel}`);
31
30
  } else if (models.length > 0) {
32
31
  defaultModel = models[0]!.id;
33
- logger.log(`Only ${models.length} models available, using ${defaultModel} as default.`);
32
+ console.log(`Only ${models.length} models available, using ${defaultModel} as default.`);
34
33
  } else {
35
- logger.log("No models available from routstr daemon, using fallback default.");
34
+ console.log("No models available from routstr daemon, using fallback default.");
36
35
  }
37
36
  } catch (error) {
38
- logger.error("Failed to fetch models for Hermes integration:", error);
39
- logger.log("Using fallback default model.");
37
+ console.error("Failed to fetch models for Hermes integration:", error);
38
+ console.log("Using fallback default model.");
40
39
  }
41
40
 
42
41
  let content = "";
@@ -45,7 +44,7 @@ export async function installHermesIntegration(
45
44
  content = await readFile(configPath, "utf-8");
46
45
  }
47
46
  } catch (error) {
48
- logger.error(`Error reading ${configPath}, creating new one.`);
47
+ console.error(`Error reading ${configPath}, creating new one.`);
49
48
  }
50
49
 
51
50
  // Remove existing model block
@@ -80,8 +79,8 @@ export async function installHermesIntegration(
80
79
  try {
81
80
  mkdirSync(dirname(configPath), { recursive: true });
82
81
  await writeFile(configPath, newContent);
83
- logger.log(`Successfully updated ${configPath} with routstr settings.`);
82
+ console.log(`Successfully updated ${configPath} with routstr settings.`);
84
83
  } catch (error) {
85
- logger.error(`Failed to write to ${configPath}:`, error);
84
+ console.error(`Failed to write to ${configPath}:`, error);
86
85
  }
87
86
  }
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { readFile, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
- import { logger } from "../utils/logger";
6
5
  import type { IntegrationConfig, RoutstrModel } from "./registry";
7
6
  import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
8
7
 
@@ -56,8 +55,8 @@ export async function installOpenClawIntegration(
56
55
  ): Promise<void> {
57
56
  const { name, configPath } = integrationConfig;
58
57
 
59
- logger.log("\nInstalling routstr models in openclaw.json...");
60
- logger.log(`Using API key for ${name}`);
58
+ console.log("\nInstalling routstr models in openclaw.json...");
59
+ console.log(`Using API key for ${name}`);
61
60
 
62
61
  const baseUrl = getDaemonBaseUrl(config);
63
62
 
@@ -92,7 +91,7 @@ export async function installOpenClawIntegration(
92
91
  const models = (data.output as { models: RoutstrModel[] } | undefined)?.models || [];
93
92
 
94
93
  if (models.length === 0) {
95
- logger.log("No models found from routstr daemon.");
94
+ console.log("No models found from routstr daemon.");
96
95
  return;
97
96
  }
98
97
 
@@ -134,8 +133,8 @@ export async function installOpenClawIntegration(
134
133
  // openclawConfig.agents.defaults.models = aliasMap;
135
134
 
136
135
  await writeFile(configPath, JSON.stringify(openclawConfig, null, 2));
137
- logger.log(`Added "${OPENCLAW_PROVIDER_ID}" provider with ${models.length} models to openclaw.json`);
136
+ console.log(`Added "${OPENCLAW_PROVIDER_ID}" provider with ${models.length} models to openclaw.json`);
138
137
  } catch (error) {
139
- logger.error("Failed to install models in openclaw.json:", error);
138
+ console.error("Failed to install models in openclaw.json:", error);
140
139
  }
141
140
  }
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { readFile, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
- import { logger } from "../utils/logger";
6
5
  import type { IntegrationConfig, RoutstrModel } from "./registry";
7
6
  import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
8
7
 
@@ -15,8 +14,8 @@ export async function installOpencodeIntegration(
15
14
  ): Promise<void> {
16
15
  const { name, configPath } = integrationConfig;
17
16
 
18
- logger.log("\nInstalling routstr models in opencode.json...");
19
- logger.log(`Using API key for ${name}`);
17
+ console.log("\nInstalling routstr models in opencode.json...");
18
+ console.log(`Using API key for ${name}`);
20
19
 
21
20
  const baseUrl = getDaemonBaseUrl(config);
22
21
 
@@ -56,7 +55,7 @@ export async function installOpencodeIntegration(
56
55
  const models = (data.output as { models: RoutstrModel[] } | undefined)?.models || [];
57
56
 
58
57
  if (models.length === 0) {
59
- logger.log("No models found from routstr daemon.");
58
+ console.log("No models found from routstr daemon.");
60
59
  return;
61
60
  }
62
61
 
@@ -78,8 +77,8 @@ export async function installOpencodeIntegration(
78
77
  opencodeConfig.small_model = OPENCODE_SMALL_MODEL;
79
78
 
80
79
  await writeFile(configPath, JSON.stringify(opencodeConfig, null, 2));
81
- logger.log(`Added "routstr" provider with ${models.length} models to opencode.json`);
80
+ console.log(`Added "routstr" provider with ${models.length} models to opencode.json`);
82
81
  } catch (error) {
83
- logger.error("Failed to install models in opencode.json:", error);
82
+ console.error("Failed to install models in opencode.json:", error);
84
83
  }
85
84
  }
@@ -2,12 +2,12 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { readFile, writeFile } from "fs/promises";
3
3
  import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
- import { logger } from "../utils/logger";
6
5
  import type { IntegrationConfig, RoutstrModel } from "./registry";
7
6
  import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
8
7
 
9
8
  type PiModelEntry = {
10
9
  id: string;
10
+ contextWindow?: number;
11
11
  };
12
12
 
13
13
  type PiProviderConfig = {
@@ -28,8 +28,8 @@ export async function installPiIntegration(
28
28
  ): Promise<void> {
29
29
  const { name, configPath } = integrationConfig;
30
30
 
31
- logger.log("\nInstalling routstr models in pi models.json...");
32
- logger.log(`Using API key for ${name}`);
31
+ console.log("\nInstalling routstr models in pi models.json...");
32
+ console.log(`Using API key for ${name}`);
33
33
 
34
34
  const baseUrl = `${getDaemonBaseUrl(config)}/v1`;
35
35
 
@@ -56,13 +56,17 @@ export async function installPiIntegration(
56
56
  const models = (data.output as { models: RoutstrModel[] } | undefined)?.models || [];
57
57
 
58
58
  if (models.length === 0) {
59
- logger.log("No models found from routstr daemon.");
59
+ console.log("No models found from routstr daemon.");
60
60
  return;
61
61
  }
62
62
 
63
- const providerModels: PiModelEntry[] = models.map((model) => ({
64
- id: model.id,
65
- }));
63
+ const providerModels: PiModelEntry[] = models.map((model) => {
64
+ const entry: PiModelEntry = { id: model.id };
65
+ if (model.context_length !== undefined && model.context_length > 0) {
66
+ entry.contextWindow = model.context_length;
67
+ }
68
+ return entry;
69
+ });
66
70
 
67
71
  piConfig.providers["routstr"] = {
68
72
  baseUrl,
@@ -72,8 +76,8 @@ export async function installPiIntegration(
72
76
  };
73
77
 
74
78
  await writeFile(configPath, JSON.stringify(piConfig, null, 2));
75
- logger.log(`Added "routstr" provider with ${models.length} models to pi models.json`);
79
+ console.log(`Added "routstr" provider with ${models.length} models to pi models.json`);
76
80
  } catch (error) {
77
- logger.error("Failed to install models in pi models.json:", error);
81
+ console.error("Failed to install models in pi models.json:", error);
78
82
  }
79
83
  }
@@ -15,6 +15,7 @@ export interface IntegrationConfig {
15
15
  export type RoutstrModel = {
16
16
  id: string;
17
17
  name?: string;
18
+ context_length?: number;
18
19
  };
19
20
 
20
21
  export type IntegrationFn = (
@@ -1,4 +1,5 @@
1
1
  import {
2
+ callAuth,
2
3
  callDaemon,
3
4
  loadConfig,
4
5
  getDaemonBaseUrl,
@@ -56,7 +57,7 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
56
57
  * Use this when running remotely (CLI in remote mode).
57
58
  */
58
59
  export async function getClientsList(): Promise<ClientEntry[]> {
59
- const result = await callDaemon("/clients");
60
+ const result = await callAuth("/clients");
60
61
  const clients = (
61
62
  result.output as
62
63
  | {
@@ -107,7 +108,7 @@ export async function addDaemonClient(
107
108
  return { client, created: false };
108
109
  }
109
110
 
110
- const result = await callDaemon("/clients/add", {
111
+ const result = await callAuth("/clients/add", {
111
112
  method: "POST",
112
113
  body: { name, id: derivedId },
113
114
  });
@@ -159,7 +160,7 @@ export async function listClientsAction(): Promise<void> {
159
160
  export async function deleteClientAction(id: string): Promise<void> {
160
161
  await ensureDaemonRunning();
161
162
 
162
- const result = await callDaemon("/clients/delete", {
163
+ const result = await callAuth("/clients/delete", {
163
164
  method: "POST",
164
165
  body: { id },
165
166
  });
@@ -35,6 +35,9 @@ export interface RoutstrdConfig {
35
35
  cocodPath: string | null;
36
36
  mode?: "xcashu" | "apikeys";
37
37
  daemonUrl?: string;
38
+ /** URL of the auth proxy (routstrd-auth) for management endpoints (npubs, clients, usage).
39
+ * Defaults to daemonUrl or localhost:{port} if not set. */
40
+ authUrl?: string;
38
41
  nsec?: string;
39
42
  /** Nostr hex pubkey for routstr review/model events (kind 38425/38423). */
40
43
  routstrPubkey?: string;
@@ -35,13 +35,20 @@ export function getDaemonBaseUrl(config: RoutstrdConfig): string {
35
35
  );
36
36
  }
37
37
 
38
- export async function callDaemon(
38
+ export function getAuthBaseUrl(config: RoutstrdConfig): string {
39
+ if (config.authUrl) {
40
+ return config.authUrl.replace(/\/$/, "");
41
+ }
42
+ return getDaemonBaseUrl(config);
43
+ }
44
+
45
+ async function _callUrl(
46
+ baseUrl: string,
39
47
  path: string,
40
- options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
48
+ options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object },
49
+ config: RoutstrdConfig,
41
50
  ): Promise<CommandResponse> {
42
51
  const { method = "GET", body } = options;
43
- const config = await loadConfig();
44
- const baseUrl = getDaemonBaseUrl(config);
45
52
  const url = `${baseUrl}${path}`;
46
53
 
47
54
  const bodyString = body ? JSON.stringify(body) : undefined;
@@ -50,7 +57,7 @@ export async function callDaemon(
50
57
  : undefined;
51
58
 
52
59
  let authorization: string | undefined;
53
- if (config.daemonUrl && config.nsec) {
60
+ if ((config.daemonUrl || config.authUrl) && config.nsec) {
54
61
  const secretKey = parseSecretKey(config.nsec);
55
62
  authorization = await createNIP98Authorization(
56
63
  secretKey,
@@ -77,6 +84,26 @@ export async function callDaemon(
77
84
  return response.json() as Promise<CommandResponse>;
78
85
  }
79
86
 
87
+ export async function callDaemon(
88
+ path: string,
89
+ options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
90
+ ): Promise<CommandResponse> {
91
+ const config = await loadConfig();
92
+ const baseUrl = getDaemonBaseUrl(config);
93
+ return _callUrl(baseUrl, path, options, config);
94
+ }
95
+
96
+ /** Like callDaemon but sends requests to the auth proxy URL instead.
97
+ * Falls back to the daemon URL if no authUrl is configured. */
98
+ export async function callAuth(
99
+ path: string,
100
+ options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
101
+ ): Promise<CommandResponse> {
102
+ const config = await loadConfig();
103
+ const baseUrl = getAuthBaseUrl(config);
104
+ return _callUrl(baseUrl, path, options, config);
105
+ }
106
+
80
107
  export async function isDaemonRunning(): Promise<boolean> {
81
108
  try {
82
109
  const config = await loadConfig();