@ottocode/server 0.1.173

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.
Files changed (111) hide show
  1. package/package.json +42 -0
  2. package/src/events/bus.ts +43 -0
  3. package/src/events/types.ts +32 -0
  4. package/src/index.ts +281 -0
  5. package/src/openapi/helpers.ts +64 -0
  6. package/src/openapi/paths/ask.ts +70 -0
  7. package/src/openapi/paths/config.ts +218 -0
  8. package/src/openapi/paths/files.ts +72 -0
  9. package/src/openapi/paths/git.ts +457 -0
  10. package/src/openapi/paths/messages.ts +92 -0
  11. package/src/openapi/paths/sessions.ts +90 -0
  12. package/src/openapi/paths/setu.ts +154 -0
  13. package/src/openapi/paths/stream.ts +26 -0
  14. package/src/openapi/paths/terminals.ts +226 -0
  15. package/src/openapi/schemas.ts +345 -0
  16. package/src/openapi/spec.ts +49 -0
  17. package/src/presets.ts +85 -0
  18. package/src/routes/ask.ts +113 -0
  19. package/src/routes/auth.ts +592 -0
  20. package/src/routes/branch.ts +106 -0
  21. package/src/routes/config/agents.ts +44 -0
  22. package/src/routes/config/cwd.ts +21 -0
  23. package/src/routes/config/defaults.ts +45 -0
  24. package/src/routes/config/index.ts +16 -0
  25. package/src/routes/config/main.ts +73 -0
  26. package/src/routes/config/models.ts +139 -0
  27. package/src/routes/config/providers.ts +46 -0
  28. package/src/routes/config/utils.ts +120 -0
  29. package/src/routes/files.ts +218 -0
  30. package/src/routes/git/branch.ts +75 -0
  31. package/src/routes/git/commit.ts +209 -0
  32. package/src/routes/git/diff.ts +137 -0
  33. package/src/routes/git/index.ts +18 -0
  34. package/src/routes/git/push.ts +160 -0
  35. package/src/routes/git/schemas.ts +48 -0
  36. package/src/routes/git/staging.ts +208 -0
  37. package/src/routes/git/status.ts +83 -0
  38. package/src/routes/git/types.ts +31 -0
  39. package/src/routes/git/utils.ts +249 -0
  40. package/src/routes/openapi.ts +6 -0
  41. package/src/routes/research.ts +392 -0
  42. package/src/routes/root.ts +5 -0
  43. package/src/routes/session-approval.ts +63 -0
  44. package/src/routes/session-files.ts +387 -0
  45. package/src/routes/session-messages.ts +170 -0
  46. package/src/routes/session-stream.ts +61 -0
  47. package/src/routes/sessions.ts +814 -0
  48. package/src/routes/setu.ts +346 -0
  49. package/src/routes/terminals.ts +227 -0
  50. package/src/runtime/agent/registry.ts +351 -0
  51. package/src/runtime/agent/runner-reasoning.ts +108 -0
  52. package/src/runtime/agent/runner-setup.ts +257 -0
  53. package/src/runtime/agent/runner.ts +375 -0
  54. package/src/runtime/agent-registry.ts +6 -0
  55. package/src/runtime/ask/service.ts +369 -0
  56. package/src/runtime/context/environment.ts +202 -0
  57. package/src/runtime/debug/index.ts +117 -0
  58. package/src/runtime/debug/state.ts +140 -0
  59. package/src/runtime/errors/api-error.ts +192 -0
  60. package/src/runtime/errors/handling.ts +199 -0
  61. package/src/runtime/message/compaction-auto.ts +154 -0
  62. package/src/runtime/message/compaction-context.ts +101 -0
  63. package/src/runtime/message/compaction-detect.ts +26 -0
  64. package/src/runtime/message/compaction-limits.ts +37 -0
  65. package/src/runtime/message/compaction-mark.ts +111 -0
  66. package/src/runtime/message/compaction-prune.ts +75 -0
  67. package/src/runtime/message/compaction.ts +21 -0
  68. package/src/runtime/message/history-builder.ts +266 -0
  69. package/src/runtime/message/service.ts +468 -0
  70. package/src/runtime/message/tool-history-tracker.ts +204 -0
  71. package/src/runtime/prompt/builder.ts +167 -0
  72. package/src/runtime/provider/anthropic.ts +50 -0
  73. package/src/runtime/provider/copilot.ts +12 -0
  74. package/src/runtime/provider/google.ts +8 -0
  75. package/src/runtime/provider/index.ts +60 -0
  76. package/src/runtime/provider/moonshot.ts +8 -0
  77. package/src/runtime/provider/oauth-adapter.ts +237 -0
  78. package/src/runtime/provider/openai.ts +18 -0
  79. package/src/runtime/provider/opencode.ts +7 -0
  80. package/src/runtime/provider/openrouter.ts +7 -0
  81. package/src/runtime/provider/selection.ts +118 -0
  82. package/src/runtime/provider/setu.ts +126 -0
  83. package/src/runtime/provider/zai.ts +16 -0
  84. package/src/runtime/session/branch.ts +280 -0
  85. package/src/runtime/session/db-operations.ts +285 -0
  86. package/src/runtime/session/manager.ts +99 -0
  87. package/src/runtime/session/queue.ts +243 -0
  88. package/src/runtime/stream/abort-handler.ts +65 -0
  89. package/src/runtime/stream/error-handler.ts +371 -0
  90. package/src/runtime/stream/finish-handler.ts +101 -0
  91. package/src/runtime/stream/handlers.ts +5 -0
  92. package/src/runtime/stream/step-finish.ts +93 -0
  93. package/src/runtime/stream/types.ts +25 -0
  94. package/src/runtime/tools/approval.ts +180 -0
  95. package/src/runtime/tools/context.ts +83 -0
  96. package/src/runtime/tools/mapping.ts +154 -0
  97. package/src/runtime/tools/setup.ts +44 -0
  98. package/src/runtime/topup/manager.ts +110 -0
  99. package/src/runtime/utils/cwd.ts +69 -0
  100. package/src/runtime/utils/token.ts +35 -0
  101. package/src/tools/adapter.ts +634 -0
  102. package/src/tools/database/get-parent-session.ts +183 -0
  103. package/src/tools/database/get-session-context.ts +161 -0
  104. package/src/tools/database/index.ts +42 -0
  105. package/src/tools/database/present-session-links.ts +47 -0
  106. package/src/tools/database/query-messages.ts +160 -0
  107. package/src/tools/database/query-sessions.ts +126 -0
  108. package/src/tools/database/search-history.ts +135 -0
  109. package/src/types/sql-imports.d.ts +5 -0
  110. package/sst-env.d.ts +8 -0
  111. package/tsconfig.json +7 -0
@@ -0,0 +1,345 @@
1
+ import { providerIds } from '@ottocode/sdk';
2
+
3
+ export const schemas = {
4
+ Provider: {
5
+ type: 'string',
6
+ enum: providerIds,
7
+ },
8
+ AskResponse: {
9
+ type: 'object',
10
+ properties: {
11
+ sessionId: { type: 'string' },
12
+ header: { $ref: '#/components/schemas/AskResponseHeader' },
13
+ provider: { $ref: '#/components/schemas/Provider' },
14
+ model: { type: 'string' },
15
+ agent: { type: 'string' },
16
+ assistantMessageId: { type: 'string' },
17
+ message: {
18
+ $ref: '#/components/schemas/AskResponseMessage',
19
+ nullable: true,
20
+ description:
21
+ 'Present when the request created a new session or reused the last session for the project.',
22
+ },
23
+ },
24
+ required: [
25
+ 'sessionId',
26
+ 'header',
27
+ 'provider',
28
+ 'model',
29
+ 'agent',
30
+ 'assistantMessageId',
31
+ ],
32
+ },
33
+ AskResponseHeader: {
34
+ type: 'object',
35
+ properties: {
36
+ sessionId: { type: 'string' },
37
+ agent: { type: 'string', nullable: true },
38
+ provider: {
39
+ $ref: '#/components/schemas/Provider',
40
+ nullable: true,
41
+ },
42
+ model: { type: 'string', nullable: true },
43
+ },
44
+ required: ['sessionId'],
45
+ },
46
+ AskResponseMessage: {
47
+ type: 'object',
48
+ properties: {
49
+ kind: { type: 'string', enum: ['created', 'last'] },
50
+ sessionId: { type: 'string' },
51
+ },
52
+ required: ['kind', 'sessionId'],
53
+ },
54
+ Session: {
55
+ type: 'object',
56
+ properties: {
57
+ id: { type: 'string' },
58
+ title: { type: 'string', nullable: true },
59
+ agent: { type: 'string' },
60
+ provider: { $ref: '#/components/schemas/Provider' },
61
+ model: { type: 'string' },
62
+ projectPath: { type: 'string' },
63
+ createdAt: { type: 'integer', format: 'int64' },
64
+ lastActiveAt: { type: 'integer', format: 'int64', nullable: true },
65
+ totalInputTokens: { type: 'integer', nullable: true },
66
+ totalOutputTokens: { type: 'integer', nullable: true },
67
+ totalCachedTokens: { type: 'integer', nullable: true },
68
+ totalCacheCreationTokens: { type: 'integer', nullable: true },
69
+ totalToolTimeMs: { type: 'integer', nullable: true },
70
+ toolCounts: {
71
+ type: 'object',
72
+ additionalProperties: { type: 'integer' },
73
+ nullable: true,
74
+ },
75
+ },
76
+ required: ['id', 'agent', 'provider', 'model', 'projectPath', 'createdAt'],
77
+ },
78
+ Message: {
79
+ type: 'object',
80
+ properties: {
81
+ id: { type: 'string' },
82
+ sessionId: { type: 'string' },
83
+ role: {
84
+ type: 'string',
85
+ enum: ['system', 'user', 'assistant', 'tool'],
86
+ },
87
+ status: { type: 'string', enum: ['pending', 'complete', 'error'] },
88
+ agent: { type: 'string' },
89
+ provider: { $ref: '#/components/schemas/Provider' },
90
+ model: { type: 'string' },
91
+ createdAt: { type: 'integer', format: 'int64' },
92
+ completedAt: { type: 'integer', format: 'int64', nullable: true },
93
+ latencyMs: { type: 'integer', nullable: true },
94
+ inputTokens: { type: 'integer', nullable: true },
95
+ outputTokens: { type: 'integer', nullable: true },
96
+ totalTokens: { type: 'integer', nullable: true },
97
+ error: { type: 'string', nullable: true },
98
+ },
99
+ required: [
100
+ 'id',
101
+ 'sessionId',
102
+ 'role',
103
+ 'status',
104
+ 'agent',
105
+ 'provider',
106
+ 'model',
107
+ 'createdAt',
108
+ ],
109
+ },
110
+ MessagePart: {
111
+ type: 'object',
112
+ properties: {
113
+ id: { type: 'string' },
114
+ messageId: { type: 'string' },
115
+ index: { type: 'integer', format: 'int64' },
116
+ type: {
117
+ type: 'string',
118
+ enum: ['text', 'tool_call', 'tool_result', 'image', 'error'],
119
+ },
120
+ content: {
121
+ type: 'string',
122
+ description:
123
+ 'JSON-encoded content. For text: {"text": string}. For tool_call: {"name": string, "args": object}. For tool_result: {"name": string, "result"?: any, "artifact"?: Artifact}.',
124
+ },
125
+ agent: { type: 'string' },
126
+ provider: { $ref: '#/components/schemas/Provider' },
127
+ model: { type: 'string' },
128
+ startedAt: { type: 'integer', format: 'int64', nullable: true },
129
+ completedAt: { type: 'integer', format: 'int64', nullable: true },
130
+ toolName: { type: 'string', nullable: true },
131
+ toolCallId: { type: 'string', nullable: true },
132
+ toolDurationMs: { type: 'integer', nullable: true },
133
+ },
134
+ required: [
135
+ 'id',
136
+ 'messageId',
137
+ 'index',
138
+ 'type',
139
+ 'content',
140
+ 'agent',
141
+ 'provider',
142
+ 'model',
143
+ ],
144
+ },
145
+ Artifact: {
146
+ oneOf: [
147
+ { $ref: '#/components/schemas/FileDiffArtifact' },
148
+ { $ref: '#/components/schemas/FileArtifact' },
149
+ ],
150
+ },
151
+ FileDiffArtifact: {
152
+ type: 'object',
153
+ properties: {
154
+ kind: { type: 'string', enum: ['file_diff'] },
155
+ patchFormat: { type: 'string', enum: ['unified'] },
156
+ patch: { type: 'string' },
157
+ summary: {
158
+ type: 'object',
159
+ properties: {
160
+ files: { type: 'integer' },
161
+ additions: { type: 'integer' },
162
+ deletions: { type: 'integer' },
163
+ },
164
+ additionalProperties: false,
165
+ },
166
+ },
167
+ required: ['kind', 'patchFormat', 'patch'],
168
+ },
169
+ FileArtifact: {
170
+ type: 'object',
171
+ properties: {
172
+ kind: { type: 'string', enum: ['file'] },
173
+ path: { type: 'string' },
174
+ mime: { type: 'string' },
175
+ size: { type: 'integer' },
176
+ sha256: { type: 'string' },
177
+ },
178
+ required: ['kind', 'path'],
179
+ },
180
+ Config: {
181
+ type: 'object',
182
+ properties: {
183
+ agents: {
184
+ type: 'array',
185
+ items: { type: 'string' },
186
+ },
187
+ providers: {
188
+ type: 'array',
189
+ items: { $ref: '#/components/schemas/Provider' },
190
+ },
191
+ defaults: {
192
+ type: 'object',
193
+ properties: {
194
+ agent: { type: 'string' },
195
+ provider: { $ref: '#/components/schemas/Provider' },
196
+ model: { type: 'string' },
197
+ },
198
+ required: ['agent', 'provider', 'model'],
199
+ },
200
+ },
201
+ required: ['agents', 'providers', 'defaults'],
202
+ },
203
+ Model: {
204
+ type: 'object',
205
+ properties: {
206
+ id: { type: 'string' },
207
+ label: { type: 'string' },
208
+ toolCall: { type: 'boolean' },
209
+ reasoningText: { type: 'boolean' },
210
+ },
211
+ required: ['id', 'label'],
212
+ },
213
+ GitStatus: {
214
+ type: 'object',
215
+ properties: {
216
+ branch: { type: 'string' },
217
+ ahead: { type: 'integer' },
218
+ behind: { type: 'integer' },
219
+ staged: {
220
+ type: 'array',
221
+ items: { $ref: '#/components/schemas/GitFile' },
222
+ },
223
+ unstaged: {
224
+ type: 'array',
225
+ items: { $ref: '#/components/schemas/GitFile' },
226
+ },
227
+ untracked: {
228
+ type: 'array',
229
+ items: { $ref: '#/components/schemas/GitFile' },
230
+ },
231
+ conflicted: {
232
+ type: 'array',
233
+ items: { $ref: '#/components/schemas/GitFile' },
234
+ },
235
+ hasChanges: { type: 'boolean' },
236
+ hasConflicts: { type: 'boolean' },
237
+ },
238
+ required: [
239
+ 'branch',
240
+ 'ahead',
241
+ 'behind',
242
+ 'staged',
243
+ 'unstaged',
244
+ 'untracked',
245
+ 'conflicted',
246
+ 'hasChanges',
247
+ 'hasConflicts',
248
+ ],
249
+ },
250
+ GitFile: {
251
+ type: 'object',
252
+ properties: {
253
+ path: { type: 'string' },
254
+ status: {
255
+ type: 'string',
256
+ enum: [
257
+ 'modified',
258
+ 'added',
259
+ 'deleted',
260
+ 'renamed',
261
+ 'untracked',
262
+ 'conflicted',
263
+ ],
264
+ },
265
+ staged: { type: 'boolean' },
266
+ insertions: { type: 'integer' },
267
+ deletions: { type: 'integer' },
268
+ oldPath: { type: 'string' },
269
+ conflictType: {
270
+ type: 'string',
271
+ enum: [
272
+ 'both-modified',
273
+ 'deleted-by-us',
274
+ 'deleted-by-them',
275
+ 'both-added',
276
+ 'both-deleted',
277
+ ],
278
+ },
279
+ },
280
+ required: ['path', 'status', 'staged'],
281
+ },
282
+ GitDiff: {
283
+ type: 'object',
284
+ properties: {
285
+ file: { type: 'string' },
286
+ diff: { type: 'string' },
287
+ insertions: { type: 'integer' },
288
+ deletions: { type: 'integer' },
289
+ language: { type: 'string' },
290
+ binary: { type: 'boolean' },
291
+ },
292
+ required: ['file', 'diff', 'insertions', 'deletions', 'language', 'binary'],
293
+ },
294
+ GitBranch: {
295
+ type: 'object',
296
+ properties: {
297
+ current: { type: 'string' },
298
+ upstream: { type: 'string' },
299
+ ahead: { type: 'integer' },
300
+ behind: { type: 'integer' },
301
+ all: {
302
+ type: 'array',
303
+ items: { type: 'string' },
304
+ },
305
+ },
306
+ required: ['current', 'upstream', 'ahead', 'behind', 'all'],
307
+ },
308
+ GitCommit: {
309
+ type: 'object',
310
+ properties: {
311
+ hash: { type: 'string' },
312
+ message: { type: 'string' },
313
+ filesChanged: { type: 'integer' },
314
+ insertions: { type: 'integer' },
315
+ deletions: { type: 'integer' },
316
+ },
317
+ required: ['hash', 'message', 'filesChanged', 'insertions', 'deletions'],
318
+ },
319
+ Terminal: {
320
+ type: 'object',
321
+ properties: {
322
+ id: { type: 'string' },
323
+ pid: { type: 'integer' },
324
+ command: { type: 'string' },
325
+ args: {
326
+ type: 'array',
327
+ items: { type: 'string' },
328
+ },
329
+ cwd: { type: 'string' },
330
+ purpose: { type: 'string' },
331
+ createdBy: {
332
+ type: 'string',
333
+ enum: ['user', 'llm'],
334
+ },
335
+ title: { type: 'string' },
336
+ status: {
337
+ type: 'string',
338
+ enum: ['running', 'exited'],
339
+ },
340
+ exitCode: { type: 'integer' },
341
+ createdAt: { type: 'string', format: 'date-time' },
342
+ uptime: { type: 'integer' },
343
+ },
344
+ },
345
+ } as const;
@@ -0,0 +1,49 @@
1
+ import { askPaths } from './paths/ask';
2
+ import { configPaths } from './paths/config';
3
+ import { filesPaths } from './paths/files';
4
+ import { gitPaths } from './paths/git';
5
+ import { messagesPaths } from './paths/messages';
6
+ import { sessionsPaths } from './paths/sessions';
7
+ import { streamPaths } from './paths/stream';
8
+ import { schemas } from './schemas';
9
+
10
+ import { terminalsPath } from './paths/terminals';
11
+ import { setuPaths } from './paths/setu';
12
+
13
+ export function getOpenAPISpec() {
14
+ const spec = {
15
+ openapi: '3.0.3',
16
+ info: {
17
+ title: 'otto server API',
18
+ version: '0.1.0',
19
+ description:
20
+ 'Server-side API for otto sessions, messages, and streaming events. All AI work runs on the server. Streaming uses SSE.',
21
+ },
22
+ tags: [
23
+ { name: 'sessions' },
24
+ { name: 'messages' },
25
+ { name: 'stream' },
26
+ { name: 'ask' },
27
+ { name: 'config' },
28
+ { name: 'files' },
29
+ { name: 'git' },
30
+ { name: 'terminals' },
31
+ { name: 'setu' },
32
+ ],
33
+ paths: {
34
+ ...askPaths,
35
+ ...sessionsPaths,
36
+ ...messagesPaths,
37
+ ...streamPaths,
38
+ ...configPaths,
39
+ ...filesPaths,
40
+ ...gitPaths,
41
+ ...terminalsPath,
42
+ ...setuPaths,
43
+ },
44
+ components: {
45
+ schemas,
46
+ },
47
+ } as const;
48
+ return spec;
49
+ }
package/src/presets.ts ADDED
@@ -0,0 +1,85 @@
1
+ import AGENT_BUILD from '@ottocode/sdk/prompts/agents/build.txt' with {
2
+ type: 'text',
3
+ };
4
+ import AGENT_PLAN from '@ottocode/sdk/prompts/agents/plan.txt' with {
5
+ type: 'text',
6
+ };
7
+ import AGENT_GENERAL from '@ottocode/sdk/prompts/agents/general.txt' with {
8
+ type: 'text',
9
+ };
10
+
11
+ export const BUILTIN_AGENTS = {
12
+ build: {
13
+ prompt: AGENT_BUILD,
14
+ tools: [
15
+ 'read',
16
+ 'write',
17
+ 'ls',
18
+ 'tree',
19
+ 'bash',
20
+ 'update_todos',
21
+ 'grep',
22
+ 'terminal',
23
+ 'git_status',
24
+ 'git_diff',
25
+ 'ripgrep',
26
+ 'apply_patch',
27
+ 'websearch',
28
+ 'progress_update',
29
+ 'finish',
30
+ ] as string[],
31
+ },
32
+ plan: {
33
+ prompt: AGENT_PLAN,
34
+ tools: [
35
+ 'read',
36
+ 'ls',
37
+ 'tree',
38
+ 'ripgrep',
39
+ 'update_todos',
40
+ 'websearch',
41
+ 'progress_update',
42
+ 'finish',
43
+ ] as string[],
44
+ },
45
+ general: {
46
+ prompt: AGENT_GENERAL,
47
+ tools: [
48
+ 'read',
49
+ 'write',
50
+ 'ls',
51
+ 'tree',
52
+ 'bash',
53
+ 'ripgrep',
54
+ 'terminal',
55
+ 'websearch',
56
+ 'update_todos',
57
+ 'progress_update',
58
+ 'finish',
59
+ ] as string[],
60
+ },
61
+ };
62
+
63
+ export const BUILTIN_TOOLS = [
64
+ 'read',
65
+ 'write',
66
+ 'ls',
67
+ 'tree',
68
+ 'bash',
69
+ 'terminal',
70
+ 'grep',
71
+ 'ripgrep',
72
+ 'git_status',
73
+ 'git_diff',
74
+ 'git_commit',
75
+ 'apply_patch',
76
+ 'update_todos',
77
+ 'edit',
78
+ 'websearch',
79
+ 'progress_update',
80
+ 'finish',
81
+ 'skill',
82
+ ] as const;
83
+
84
+ export type BuiltinAgent = keyof typeof BUILTIN_AGENTS;
85
+ export type BuiltinTool = (typeof BUILTIN_TOOLS)[number];
@@ -0,0 +1,113 @@
1
+ import type { Hono } from 'hono';
2
+ import type {
3
+ AskServerRequest,
4
+ InjectableConfig,
5
+ InjectableCredentials,
6
+ } from '../runtime/ask/service.ts';
7
+ import { handleAskRequest } from '../runtime/ask/service.ts';
8
+ import { serializeError } from '../runtime/errors/api-error.ts';
9
+ import { logger } from '@ottocode/sdk';
10
+ import type { EmbeddedAppConfig } from '../index.ts';
11
+
12
+ export function registerAskRoutes(app: Hono) {
13
+ app.post('/v1/ask', async (c) => {
14
+ const projectRoot = c.req.query('project') || process.cwd();
15
+ const body = (await c.req.json().catch(() => ({}))) as Record<
16
+ string,
17
+ unknown
18
+ >;
19
+ const prompt = typeof body.prompt === 'string' ? body.prompt : '';
20
+ if (!prompt.trim().length) {
21
+ return c.json({ error: 'Prompt is required.' }, 400);
22
+ }
23
+
24
+ const embeddedConfig = c.get('embeddedConfig') as
25
+ | EmbeddedAppConfig
26
+ | undefined;
27
+
28
+ // Hybrid fallback: Use embedded config if provided, otherwise fall back to files/env
29
+ let injectableConfig: InjectableConfig | undefined;
30
+ let injectableCredentials: InjectableCredentials | undefined;
31
+ let skipFileConfig = false;
32
+
33
+ if (embeddedConfig && Object.keys(embeddedConfig).length > 0) {
34
+ // Has embedded config - build injectable config from it
35
+ const defaults = embeddedConfig.defaults;
36
+ const hasDefaults =
37
+ defaults ||
38
+ embeddedConfig.provider ||
39
+ embeddedConfig.model ||
40
+ embeddedConfig.agent;
41
+
42
+ if (hasDefaults) {
43
+ injectableConfig = {
44
+ provider: defaults?.provider ?? embeddedConfig.provider,
45
+ model: defaults?.model ?? embeddedConfig.model,
46
+ agent: defaults?.agent ?? embeddedConfig.agent,
47
+ };
48
+ }
49
+
50
+ // Convert embedded auth to injectable credentials
51
+ const hasAuth = embeddedConfig.auth || embeddedConfig.apiKey;
52
+ if (hasAuth) {
53
+ if (embeddedConfig.auth) {
54
+ injectableCredentials = {} as InjectableCredentials;
55
+ for (const [provider, auth] of Object.entries(embeddedConfig.auth)) {
56
+ if ('apiKey' in auth) {
57
+ (injectableCredentials as Record<string, { apiKey: string }>)[
58
+ provider
59
+ ] = { apiKey: auth.apiKey };
60
+ }
61
+ }
62
+ } else if (embeddedConfig.apiKey && embeddedConfig.provider) {
63
+ injectableCredentials = {
64
+ [embeddedConfig.provider]: { apiKey: embeddedConfig.apiKey },
65
+ };
66
+ }
67
+
68
+ // Only skip file config if we have credentials injected
69
+ skipFileConfig = true;
70
+ }
71
+ // If no auth provided, skipFileConfig stays false -> will use ensureProviderEnv -> auth.json fallback
72
+ }
73
+
74
+ const request: AskServerRequest = {
75
+ projectRoot,
76
+ prompt,
77
+ agent: typeof body.agent === 'string' ? body.agent : undefined,
78
+ provider: typeof body.provider === 'string' ? body.provider : undefined,
79
+ model: typeof body.model === 'string' ? body.model : undefined,
80
+ sessionId:
81
+ typeof body.sessionId === 'string' ? body.sessionId : undefined,
82
+ last: Boolean(body.last),
83
+ jsonMode: Boolean(body.jsonMode),
84
+ skipFileConfig:
85
+ skipFileConfig ||
86
+ (typeof body.skipFileConfig === 'boolean'
87
+ ? body.skipFileConfig
88
+ : false),
89
+ config:
90
+ injectableConfig ||
91
+ (body.config && typeof body.config === 'object'
92
+ ? (body.config as InjectableConfig)
93
+ : undefined),
94
+ credentials:
95
+ injectableCredentials ||
96
+ (body.credentials && typeof body.credentials === 'object'
97
+ ? (body.credentials as InjectableCredentials)
98
+ : undefined),
99
+ agentPrompt:
100
+ typeof body.agentPrompt === 'string' ? body.agentPrompt : undefined,
101
+ tools: Array.isArray(body.tools) ? body.tools : undefined,
102
+ };
103
+
104
+ try {
105
+ const response = await handleAskRequest(request);
106
+ return c.json(response, 202);
107
+ } catch (err) {
108
+ logger.error('Ask request failed', err);
109
+ const errorResponse = serializeError(err);
110
+ return c.json(errorResponse, errorResponse.error.status || 400);
111
+ }
112
+ });
113
+ }