@agi-cli/server 0.1.80 → 0.1.82
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 +3 -3
- package/src/index.ts +6 -2
- package/src/openapi/helpers.ts +64 -0
- package/src/openapi/paths/ask.ts +70 -0
- package/src/openapi/paths/config.ts +164 -0
- package/src/openapi/paths/files.ts +72 -0
- package/src/openapi/paths/git.ts +453 -0
- package/src/openapi/paths/messages.ts +92 -0
- package/src/openapi/paths/sessions.ts +90 -0
- package/src/openapi/paths/stream.ts +26 -0
- package/src/openapi/schemas.ts +293 -0
- package/src/openapi/spec.ts +17 -1142
- package/src/routes/config/agents.ts +44 -0
- package/src/routes/config/cwd.ts +21 -0
- package/src/routes/config/index.ts +14 -0
- package/src/routes/config/main.ts +68 -0
- package/src/routes/config/models.ts +109 -0
- package/src/routes/config/providers.ts +46 -0
- package/src/routes/config/utils.ts +107 -0
- package/src/routes/files.ts +218 -0
- package/src/routes/git/branch.ts +75 -0
- package/src/routes/git/commit.ts +159 -0
- package/src/routes/git/diff.ts +137 -0
- package/src/routes/git/index.ts +18 -0
- package/src/routes/git/push.ts +160 -0
- package/src/routes/git/schemas.ts +47 -0
- package/src/routes/git/staging.ts +208 -0
- package/src/routes/git/status.ts +76 -0
- package/src/routes/git/types.ts +19 -0
- package/src/routes/git/utils.ts +212 -0
- package/src/runtime/runner.ts +3 -6
- package/src/runtime/session-manager.ts +1 -1
- package/src/routes/config.ts +0 -387
- package/src/routes/git.ts +0 -980
package/src/routes/config.ts
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import type { Hono } from 'hono';
|
|
2
|
-
import { loadConfig } from '@agi-cli/sdk';
|
|
3
|
-
import {
|
|
4
|
-
catalog,
|
|
5
|
-
type ProviderId,
|
|
6
|
-
isProviderAuthorized,
|
|
7
|
-
getGlobalAgentsDir,
|
|
8
|
-
} from '@agi-cli/sdk';
|
|
9
|
-
import { readdir } from 'node:fs/promises';
|
|
10
|
-
import { join, basename } from 'node:path';
|
|
11
|
-
import type { EmbeddedAppConfig } from '../index.ts';
|
|
12
|
-
import type { AGIConfig } from '@agi-cli/sdk';
|
|
13
|
-
import { logger } from '../runtime/logger.ts';
|
|
14
|
-
import { serializeError } from '../runtime/api-error.ts';
|
|
15
|
-
import { loadAgentsConfig } from '../runtime/agent-registry.ts';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Check if a provider is authorized in either embedded config or file-based config
|
|
19
|
-
*/
|
|
20
|
-
async function isProviderAuthorizedHybrid(
|
|
21
|
-
embeddedConfig: EmbeddedAppConfig | undefined,
|
|
22
|
-
fileConfig: AGIConfig,
|
|
23
|
-
provider: ProviderId,
|
|
24
|
-
): Promise<boolean> {
|
|
25
|
-
// Check embedded auth first
|
|
26
|
-
const hasEmbeddedAuth =
|
|
27
|
-
embeddedConfig?.provider === provider ||
|
|
28
|
-
(embeddedConfig?.auth && provider in embeddedConfig.auth);
|
|
29
|
-
|
|
30
|
-
if (hasEmbeddedAuth) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Fallback to file-based auth
|
|
35
|
-
return await isProviderAuthorized(fileConfig, provider);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Get all authorized providers from both embedded and file-based config
|
|
40
|
-
*/
|
|
41
|
-
async function getAuthorizedProviders(
|
|
42
|
-
embeddedConfig: EmbeddedAppConfig | undefined,
|
|
43
|
-
fileConfig: AGIConfig,
|
|
44
|
-
): Promise<ProviderId[]> {
|
|
45
|
-
const allProviders = Object.keys(catalog) as ProviderId[];
|
|
46
|
-
const authorizedProviders: ProviderId[] = [];
|
|
47
|
-
|
|
48
|
-
for (const provider of allProviders) {
|
|
49
|
-
const authorized = await isProviderAuthorizedHybrid(
|
|
50
|
-
embeddedConfig,
|
|
51
|
-
fileConfig,
|
|
52
|
-
provider,
|
|
53
|
-
);
|
|
54
|
-
if (authorized) {
|
|
55
|
-
authorizedProviders.push(provider);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return authorizedProviders;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get default value with embedded config taking priority over file config
|
|
64
|
-
*/
|
|
65
|
-
function getDefault<T>(
|
|
66
|
-
embeddedValue: T | undefined,
|
|
67
|
-
embeddedDefaultValue: T | undefined,
|
|
68
|
-
fileValue: T,
|
|
69
|
-
): T {
|
|
70
|
-
return embeddedValue ?? embeddedDefaultValue ?? fileValue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Discover all agents from all sources:
|
|
75
|
-
* - Built-in agents (general, build, plan)
|
|
76
|
-
* - agents.json (global + local)
|
|
77
|
-
* - Agent files in .agi/agents/ (global + local)
|
|
78
|
-
*/
|
|
79
|
-
async function discoverAllAgents(projectRoot: string): Promise<string[]> {
|
|
80
|
-
const builtInAgents = ['general', 'build', 'plan'];
|
|
81
|
-
const agentSet = new Set<string>(builtInAgents);
|
|
82
|
-
|
|
83
|
-
// Load agents from agents.json (global + local merged)
|
|
84
|
-
try {
|
|
85
|
-
const agentsJson = await loadAgentsConfig(projectRoot);
|
|
86
|
-
for (const agentName of Object.keys(agentsJson)) {
|
|
87
|
-
if (agentName.trim()) {
|
|
88
|
-
agentSet.add(agentName);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} catch (err) {
|
|
92
|
-
logger.debug('Failed to load agents.json', err);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Discover custom agent files from local .agi/agents/
|
|
96
|
-
try {
|
|
97
|
-
const localAgentsPath = join(projectRoot, '.agi', 'agents');
|
|
98
|
-
const localFiles = await readdir(localAgentsPath).catch(() => []);
|
|
99
|
-
for (const file of localFiles) {
|
|
100
|
-
if (file.endsWith('.txt') || file.endsWith('.md')) {
|
|
101
|
-
const agentName = file.replace(/\.(txt|md)$/, '');
|
|
102
|
-
if (agentName.trim()) {
|
|
103
|
-
agentSet.add(agentName);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch (err) {
|
|
108
|
-
logger.debug('Failed to read local agents directory', err);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Discover custom agent files from global ~/.config/agi/agents/
|
|
112
|
-
try {
|
|
113
|
-
const globalAgentsPath = getGlobalAgentsDir();
|
|
114
|
-
const globalFiles = await readdir(globalAgentsPath).catch(() => []);
|
|
115
|
-
for (const file of globalFiles) {
|
|
116
|
-
if (file.endsWith('.txt') || file.endsWith('.md')) {
|
|
117
|
-
const agentName = file.replace(/\.(txt|md)$/, '');
|
|
118
|
-
if (agentName.trim()) {
|
|
119
|
-
agentSet.add(agentName);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
} catch (err) {
|
|
124
|
-
logger.debug('Failed to read global agents directory', err);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return Array.from(agentSet).sort();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function registerConfigRoutes(app: Hono) {
|
|
131
|
-
// Get working directory info
|
|
132
|
-
app.get('/v1/config/cwd', (c) => {
|
|
133
|
-
try {
|
|
134
|
-
const cwd = process.cwd();
|
|
135
|
-
const dirName = basename(cwd);
|
|
136
|
-
return c.json({
|
|
137
|
-
cwd,
|
|
138
|
-
dirName,
|
|
139
|
-
});
|
|
140
|
-
} catch (error) {
|
|
141
|
-
logger.error('Failed to get current working directory', error);
|
|
142
|
-
const errorResponse = serializeError(error);
|
|
143
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Get full config (agents, providers, models, defaults)
|
|
148
|
-
app.get('/v1/config', async (c) => {
|
|
149
|
-
try {
|
|
150
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
151
|
-
const embeddedConfig = c.get('embeddedConfig') as
|
|
152
|
-
| EmbeddedAppConfig
|
|
153
|
-
| undefined;
|
|
154
|
-
|
|
155
|
-
// Always load file config as base/fallback
|
|
156
|
-
const cfg = await loadConfig(projectRoot);
|
|
157
|
-
|
|
158
|
-
// Hybrid mode: Merge embedded config with file config
|
|
159
|
-
let allAgents: string[];
|
|
160
|
-
|
|
161
|
-
if (embeddedConfig?.agents) {
|
|
162
|
-
// Embedded mode: use embedded agents + file agents
|
|
163
|
-
const embeddedAgents = Object.keys(embeddedConfig.agents);
|
|
164
|
-
const fileAgents = await discoverAllAgents(cfg.projectRoot);
|
|
165
|
-
allAgents = Array.from(
|
|
166
|
-
new Set([...embeddedAgents, ...fileAgents]),
|
|
167
|
-
).sort();
|
|
168
|
-
} else {
|
|
169
|
-
// File mode: discover all agents
|
|
170
|
-
allAgents = await discoverAllAgents(cfg.projectRoot);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Providers: Check both embedded and file-based auth
|
|
174
|
-
const authorizedProviders = await getAuthorizedProviders(
|
|
175
|
-
embeddedConfig,
|
|
176
|
-
cfg,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Defaults: Embedded overrides file config
|
|
180
|
-
const defaults = {
|
|
181
|
-
agent: getDefault(
|
|
182
|
-
embeddedConfig?.agent,
|
|
183
|
-
embeddedConfig?.defaults?.agent,
|
|
184
|
-
cfg.defaults.agent,
|
|
185
|
-
),
|
|
186
|
-
provider: getDefault(
|
|
187
|
-
embeddedConfig?.provider,
|
|
188
|
-
embeddedConfig?.defaults?.provider,
|
|
189
|
-
cfg.defaults.provider,
|
|
190
|
-
),
|
|
191
|
-
model: getDefault(
|
|
192
|
-
embeddedConfig?.model,
|
|
193
|
-
embeddedConfig?.defaults?.model,
|
|
194
|
-
cfg.defaults.model,
|
|
195
|
-
),
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
return c.json({
|
|
199
|
-
agents: allAgents,
|
|
200
|
-
providers: authorizedProviders,
|
|
201
|
-
defaults,
|
|
202
|
-
});
|
|
203
|
-
} catch (error) {
|
|
204
|
-
logger.error('Failed to load config', error);
|
|
205
|
-
const errorResponse = serializeError(error);
|
|
206
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Get available agents
|
|
211
|
-
app.get('/v1/config/agents', async (c) => {
|
|
212
|
-
try {
|
|
213
|
-
const embeddedConfig = c.get('embeddedConfig') as
|
|
214
|
-
| EmbeddedAppConfig
|
|
215
|
-
| undefined;
|
|
216
|
-
|
|
217
|
-
if (embeddedConfig) {
|
|
218
|
-
const agents = embeddedConfig.agents
|
|
219
|
-
? Object.keys(embeddedConfig.agents)
|
|
220
|
-
: ['general', 'build', 'plan'];
|
|
221
|
-
return c.json({
|
|
222
|
-
agents,
|
|
223
|
-
default: getDefault(
|
|
224
|
-
embeddedConfig.agent,
|
|
225
|
-
embeddedConfig.defaults?.agent,
|
|
226
|
-
'general',
|
|
227
|
-
),
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
232
|
-
const cfg = await loadConfig(projectRoot);
|
|
233
|
-
|
|
234
|
-
const allAgents = await discoverAllAgents(cfg.projectRoot);
|
|
235
|
-
|
|
236
|
-
return c.json({
|
|
237
|
-
agents: allAgents,
|
|
238
|
-
default: cfg.defaults.agent,
|
|
239
|
-
});
|
|
240
|
-
} catch (error) {
|
|
241
|
-
logger.error('Failed to get agents', error);
|
|
242
|
-
const errorResponse = serializeError(error);
|
|
243
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Get available providers (only authorized ones)
|
|
248
|
-
app.get('/v1/config/providers', async (c) => {
|
|
249
|
-
try {
|
|
250
|
-
const embeddedConfig = c.get('embeddedConfig') as
|
|
251
|
-
| EmbeddedAppConfig
|
|
252
|
-
| undefined;
|
|
253
|
-
|
|
254
|
-
if (embeddedConfig) {
|
|
255
|
-
const providers = embeddedConfig.auth
|
|
256
|
-
? (Object.keys(embeddedConfig.auth) as ProviderId[])
|
|
257
|
-
: [embeddedConfig.provider];
|
|
258
|
-
|
|
259
|
-
return c.json({
|
|
260
|
-
providers,
|
|
261
|
-
default: getDefault(
|
|
262
|
-
embeddedConfig.provider,
|
|
263
|
-
embeddedConfig.defaults?.provider,
|
|
264
|
-
undefined,
|
|
265
|
-
),
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
270
|
-
const cfg = await loadConfig(projectRoot);
|
|
271
|
-
|
|
272
|
-
const authorizedProviders = await getAuthorizedProviders(undefined, cfg);
|
|
273
|
-
|
|
274
|
-
return c.json({
|
|
275
|
-
providers: authorizedProviders,
|
|
276
|
-
default: cfg.defaults.provider,
|
|
277
|
-
});
|
|
278
|
-
} catch (error) {
|
|
279
|
-
logger.error('Failed to get providers', error);
|
|
280
|
-
const errorResponse = serializeError(error);
|
|
281
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// Get available models for a provider
|
|
286
|
-
app.get('/v1/config/providers/:provider/models', async (c) => {
|
|
287
|
-
try {
|
|
288
|
-
const embeddedConfig = c.get('embeddedConfig') as
|
|
289
|
-
| EmbeddedAppConfig
|
|
290
|
-
| undefined;
|
|
291
|
-
const provider = c.req.param('provider') as ProviderId;
|
|
292
|
-
|
|
293
|
-
// Always load file config for fallback auth check
|
|
294
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
295
|
-
const cfg = await loadConfig(projectRoot);
|
|
296
|
-
|
|
297
|
-
// Check if provider is authorized (hybrid: embedded OR file-based)
|
|
298
|
-
const authorized = await isProviderAuthorizedHybrid(
|
|
299
|
-
embeddedConfig,
|
|
300
|
-
cfg,
|
|
301
|
-
provider,
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
if (!authorized) {
|
|
305
|
-
logger.warn('Provider not authorized', { provider });
|
|
306
|
-
return c.json({ error: 'Provider not authorized' }, 403);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const providerCatalog = catalog[provider];
|
|
310
|
-
if (!providerCatalog) {
|
|
311
|
-
logger.warn('Provider not found in catalog', { provider });
|
|
312
|
-
return c.json({ error: 'Provider not found' }, 404);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return c.json({
|
|
316
|
-
models: providerCatalog.models.map((m) => ({
|
|
317
|
-
id: m.id,
|
|
318
|
-
label: m.label || m.id,
|
|
319
|
-
toolCall: m.toolCall,
|
|
320
|
-
reasoning: m.reasoning,
|
|
321
|
-
})),
|
|
322
|
-
default: getDefault(
|
|
323
|
-
embeddedConfig?.model,
|
|
324
|
-
embeddedConfig?.defaults?.model,
|
|
325
|
-
cfg.defaults.model,
|
|
326
|
-
),
|
|
327
|
-
});
|
|
328
|
-
} catch (error) {
|
|
329
|
-
logger.error('Failed to get provider models', error);
|
|
330
|
-
const errorResponse = serializeError(error);
|
|
331
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// Get all models grouped by provider
|
|
336
|
-
app.get('/v1/config/models', async (c) => {
|
|
337
|
-
try {
|
|
338
|
-
const embeddedConfig = c.get('embeddedConfig') as
|
|
339
|
-
| EmbeddedAppConfig
|
|
340
|
-
| undefined;
|
|
341
|
-
|
|
342
|
-
const projectRoot = c.req.query('project') || process.cwd();
|
|
343
|
-
const cfg = await loadConfig(projectRoot);
|
|
344
|
-
|
|
345
|
-
// Get all authorized providers
|
|
346
|
-
const authorizedProviders = await getAuthorizedProviders(
|
|
347
|
-
embeddedConfig,
|
|
348
|
-
cfg,
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// Build models map
|
|
352
|
-
const modelsMap: Record<
|
|
353
|
-
string,
|
|
354
|
-
{
|
|
355
|
-
label: string;
|
|
356
|
-
models: Array<{
|
|
357
|
-
id: string;
|
|
358
|
-
label: string;
|
|
359
|
-
toolCall?: boolean;
|
|
360
|
-
reasoning?: boolean;
|
|
361
|
-
}>;
|
|
362
|
-
}
|
|
363
|
-
> = {};
|
|
364
|
-
|
|
365
|
-
for (const provider of authorizedProviders) {
|
|
366
|
-
const providerCatalog = catalog[provider];
|
|
367
|
-
if (providerCatalog) {
|
|
368
|
-
modelsMap[provider] = {
|
|
369
|
-
label: providerCatalog.label || provider,
|
|
370
|
-
models: providerCatalog.models.map((m) => ({
|
|
371
|
-
id: m.id,
|
|
372
|
-
label: m.label || m.id,
|
|
373
|
-
toolCall: m.toolCall,
|
|
374
|
-
reasoning: m.reasoning,
|
|
375
|
-
})),
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return c.json(modelsMap);
|
|
381
|
-
} catch (error) {
|
|
382
|
-
logger.error('Failed to get all models', error);
|
|
383
|
-
const errorResponse = serializeError(error);
|
|
384
|
-
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
}
|