@agi-cli/server 0.1.64 → 0.1.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agi-cli/server",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "HTTP API server for AGI CLI",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -29,10 +29,11 @@
29
29
  "typecheck": "tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "@agi-cli/sdk": "0.1.64",
33
- "@agi-cli/database": "0.1.64",
32
+ "@agi-cli/sdk": "0.1.66",
33
+ "@agi-cli/database": "0.1.66",
34
34
  "drizzle-orm": "^0.44.5",
35
- "hono": "^4.9.9"
35
+ "hono": "^4.9.9",
36
+ "zod": "^4.1.8"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/bun": "latest",
@@ -4,6 +4,63 @@ import { catalog, type ProviderId, isProviderAuthorized } from '@agi-cli/sdk';
4
4
  import { readdir } from 'node:fs/promises';
5
5
  import { join, basename } from 'node:path';
6
6
  import type { EmbeddedAppConfig } from '../index.ts';
7
+ import type { AGIConfig } from '@agi-cli/sdk';
8
+
9
+ /**
10
+ * Check if a provider is authorized in either embedded config or file-based config
11
+ */
12
+ async function isProviderAuthorizedHybrid(
13
+ embeddedConfig: EmbeddedAppConfig | undefined,
14
+ fileConfig: AGIConfig,
15
+ provider: ProviderId,
16
+ ): Promise<boolean> {
17
+ // Check embedded auth first
18
+ const hasEmbeddedAuth =
19
+ embeddedConfig?.provider === provider ||
20
+ (embeddedConfig?.auth && provider in embeddedConfig.auth);
21
+
22
+ if (hasEmbeddedAuth) {
23
+ return true;
24
+ }
25
+
26
+ // Fallback to file-based auth
27
+ return await isProviderAuthorized(fileConfig, provider);
28
+ }
29
+
30
+ /**
31
+ * Get all authorized providers from both embedded and file-based config
32
+ */
33
+ async function getAuthorizedProviders(
34
+ embeddedConfig: EmbeddedAppConfig | undefined,
35
+ fileConfig: AGIConfig,
36
+ ): Promise<ProviderId[]> {
37
+ const allProviders = Object.keys(catalog) as ProviderId[];
38
+ const authorizedProviders: ProviderId[] = [];
39
+
40
+ for (const provider of allProviders) {
41
+ const authorized = await isProviderAuthorizedHybrid(
42
+ embeddedConfig,
43
+ fileConfig,
44
+ provider,
45
+ );
46
+ if (authorized) {
47
+ authorizedProviders.push(provider);
48
+ }
49
+ }
50
+
51
+ return authorizedProviders;
52
+ }
53
+
54
+ /**
55
+ * Get default value with embedded config taking priority over file config
56
+ */
57
+ function getDefault<T>(
58
+ embeddedValue: T | undefined,
59
+ embeddedDefaultValue: T | undefined,
60
+ fileValue: T,
61
+ ): T {
62
+ return embeddedValue ?? embeddedDefaultValue ?? fileValue;
63
+ }
7
64
 
8
65
  export function registerConfigRoutes(app: Hono) {
9
66
  // Get working directory info
@@ -46,37 +103,28 @@ export function registerConfigRoutes(app: Hono) {
46
103
  const allAgents = Array.from(new Set([...embeddedAgents, ...fileAgents]));
47
104
 
48
105
  // Providers: Check both embedded and file-based auth
49
- const allProviders = Object.keys(catalog) as ProviderId[];
50
- const authorizedProviders: ProviderId[] = [];
51
-
52
- for (const provider of allProviders) {
53
- // Check embedded auth first
54
- const hasEmbeddedAuth =
55
- embeddedConfig?.provider === provider ||
56
- (embeddedConfig?.auth && provider in embeddedConfig.auth);
57
-
58
- // Fallback to file-based auth
59
- const hasFileAuth = await isProviderAuthorized(cfg, provider);
60
-
61
- if (hasEmbeddedAuth || hasFileAuth) {
62
- authorizedProviders.push(provider);
63
- }
64
- }
106
+ const authorizedProviders = await getAuthorizedProviders(
107
+ embeddedConfig,
108
+ cfg,
109
+ );
65
110
 
66
111
  // Defaults: Embedded overrides file config
67
112
  const defaults = {
68
- agent:
69
- embeddedConfig?.defaults?.agent ||
70
- embeddedConfig?.agent ||
113
+ agent: getDefault(
114
+ embeddedConfig?.agent,
115
+ embeddedConfig?.defaults?.agent,
71
116
  cfg.defaults.agent,
72
- provider:
73
- embeddedConfig?.defaults?.provider ||
74
- embeddedConfig?.provider ||
117
+ ),
118
+ provider: getDefault(
119
+ embeddedConfig?.provider,
120
+ embeddedConfig?.defaults?.provider,
75
121
  cfg.defaults.provider,
76
- model:
77
- embeddedConfig?.defaults?.model ||
78
- embeddedConfig?.model ||
122
+ ),
123
+ model: getDefault(
124
+ embeddedConfig?.model,
125
+ embeddedConfig?.defaults?.model,
79
126
  cfg.defaults.model,
127
+ ),
80
128
  };
81
129
 
82
130
  return c.json({
@@ -98,8 +146,11 @@ export function registerConfigRoutes(app: Hono) {
98
146
  : ['general', 'build', 'plan'];
99
147
  return c.json({
100
148
  agents,
101
- default:
102
- embeddedConfig.agent || embeddedConfig.defaults?.agent || 'general',
149
+ default: getDefault(
150
+ embeddedConfig.agent,
151
+ embeddedConfig.defaults?.agent,
152
+ 'general',
153
+ ),
103
154
  });
104
155
  }
105
156
 
@@ -140,22 +191,18 @@ export function registerConfigRoutes(app: Hono) {
140
191
 
141
192
  return c.json({
142
193
  providers,
143
- default: embeddedConfig.defaults?.provider || embeddedConfig.provider,
194
+ default: getDefault(
195
+ embeddedConfig.provider,
196
+ embeddedConfig.defaults?.provider,
197
+ undefined,
198
+ ),
144
199
  });
145
200
  }
146
201
 
147
202
  const projectRoot = c.req.query('project') || process.cwd();
148
203
  const cfg = await loadConfig(projectRoot);
149
204
 
150
- const allProviders = Object.keys(catalog) as ProviderId[];
151
- const authorizedProviders: ProviderId[] = [];
152
-
153
- for (const provider of allProviders) {
154
- const authorized = await isProviderAuthorized(cfg, provider);
155
- if (authorized) {
156
- authorizedProviders.push(provider);
157
- }
158
- }
205
+ const authorizedProviders = await getAuthorizedProviders(undefined, cfg);
159
206
 
160
207
  return c.json({
161
208
  providers: authorizedProviders,
@@ -170,36 +217,17 @@ export function registerConfigRoutes(app: Hono) {
170
217
  | undefined;
171
218
  const provider = c.req.param('provider') as ProviderId;
172
219
 
173
- if (embeddedConfig) {
174
- // Check if provider is authorized in embedded mode
175
- const hasAuth =
176
- embeddedConfig.provider === provider ||
177
- (embeddedConfig.auth && provider in embeddedConfig.auth);
178
-
179
- if (!hasAuth) {
180
- return c.json({ error: 'Provider not authorized' }, 403);
181
- }
182
-
183
- const providerCatalog = catalog[provider];
184
- if (!providerCatalog) {
185
- return c.json({ error: 'Provider not found' }, 404);
186
- }
187
-
188
- return c.json({
189
- models: providerCatalog.models.map((m) => ({
190
- id: m.id,
191
- label: m.label || m.id,
192
- toolCall: m.toolCall,
193
- reasoning: m.reasoning,
194
- })),
195
- default: embeddedConfig.model || embeddedConfig.defaults?.model,
196
- });
197
- }
198
-
220
+ // Always load file config for fallback auth check
199
221
  const projectRoot = c.req.query('project') || process.cwd();
200
222
  const cfg = await loadConfig(projectRoot);
201
223
 
202
- const authorized = await isProviderAuthorized(cfg, provider);
224
+ // Check if provider is authorized (hybrid: embedded OR file-based)
225
+ const authorized = await isProviderAuthorizedHybrid(
226
+ embeddedConfig,
227
+ cfg,
228
+ provider,
229
+ );
230
+
203
231
  if (!authorized) {
204
232
  return c.json({ error: 'Provider not authorized' }, 403);
205
233
  }
@@ -216,7 +244,11 @@ export function registerConfigRoutes(app: Hono) {
216
244
  toolCall: m.toolCall,
217
245
  reasoning: m.reasoning,
218
246
  })),
219
- default: cfg.defaults.model,
247
+ default: getDefault(
248
+ embeddedConfig?.model,
249
+ embeddedConfig?.defaults?.model,
250
+ cfg.defaults.model,
251
+ ),
220
252
  });
221
253
  });
222
254
  }