@ottocode/server 0.1.259 → 0.1.261
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 +4 -3
- package/src/index.ts +5 -4
- package/src/openapi/register.ts +92 -0
- package/src/openapi/route.ts +22 -0
- package/src/routes/ask.ts +210 -99
- package/src/routes/auth.ts +1701 -626
- package/src/routes/branch.ts +281 -90
- package/src/routes/config/agents.ts +79 -32
- package/src/routes/config/cwd.ts +46 -14
- package/src/routes/config/debug.ts +159 -30
- package/src/routes/config/defaults.ts +182 -64
- package/src/routes/config/main.ts +109 -73
- package/src/routes/config/models.ts +304 -137
- package/src/routes/config/providers.ts +462 -166
- package/src/routes/config/utils.ts +2 -2
- package/src/routes/doctor.ts +395 -161
- package/src/routes/files.ts +650 -260
- package/src/routes/git/branch.ts +143 -52
- package/src/routes/git/commit.ts +347 -141
- package/src/routes/git/diff.ts +239 -116
- package/src/routes/git/init.ts +103 -23
- package/src/routes/git/pull.ts +167 -65
- package/src/routes/git/push.ts +222 -117
- package/src/routes/git/remote.ts +401 -100
- package/src/routes/git/staging.ts +502 -141
- package/src/routes/git/status.ts +171 -78
- package/src/routes/mcp.ts +1129 -404
- package/src/routes/openapi.ts +27 -4
- package/src/routes/ottorouter.ts +1221 -389
- package/src/routes/provider-usage.ts +153 -36
- package/src/routes/research.ts +817 -370
- package/src/routes/root.ts +50 -6
- package/src/routes/session-approval.ts +228 -54
- package/src/routes/session-files.ts +265 -134
- package/src/routes/session-messages.ts +330 -150
- package/src/routes/session-stream.ts +83 -2
- package/src/routes/sessions.ts +1830 -780
- package/src/routes/skills.ts +849 -161
- package/src/routes/terminals.ts +469 -103
- package/src/routes/tunnel.ts +394 -118
- package/src/runtime/agent/runner-reasoning.ts +38 -3
- package/src/runtime/agent/runner.ts +1 -0
- package/src/runtime/ask/service.ts +1 -0
- package/src/runtime/message/compaction-limits.ts +3 -3
- package/src/runtime/provider/reasoning.ts +18 -7
- package/src/runtime/session/db-operations.ts +4 -3
- package/src/runtime/utils/token.ts +7 -2
- package/src/tools/adapter.ts +21 -0
- package/src/openapi/paths/ask.ts +0 -81
- package/src/openapi/paths/auth.ts +0 -687
- package/src/openapi/paths/branch.ts +0 -102
- package/src/openapi/paths/config.ts +0 -485
- package/src/openapi/paths/doctor.ts +0 -165
- package/src/openapi/paths/files.ts +0 -236
- package/src/openapi/paths/git.ts +0 -690
- package/src/openapi/paths/mcp.ts +0 -339
- package/src/openapi/paths/messages.ts +0 -103
- package/src/openapi/paths/ottorouter.ts +0 -594
- package/src/openapi/paths/provider-usage.ts +0 -59
- package/src/openapi/paths/research.ts +0 -227
- package/src/openapi/paths/session-approval.ts +0 -93
- package/src/openapi/paths/session-extras.ts +0 -336
- package/src/openapi/paths/session-files.ts +0 -91
- package/src/openapi/paths/sessions.ts +0 -210
- package/src/openapi/paths/skills.ts +0 -377
- package/src/openapi/paths/stream.ts +0 -26
- package/src/openapi/paths/terminals.ts +0 -226
- package/src/openapi/paths/tunnel.ts +0 -163
- package/src/openapi/spec.ts +0 -73
package/src/routes/git/commit.ts
CHANGED
|
@@ -20,127 +20,332 @@ import {
|
|
|
20
20
|
adaptSimpleCall,
|
|
21
21
|
} from '../../runtime/provider/oauth-adapter.ts';
|
|
22
22
|
import { appendCoAuthorTrailer } from '@ottocode/sdk';
|
|
23
|
+
import { openApiRoute } from '../../openapi/route.ts';
|
|
23
24
|
|
|
24
25
|
const execFileAsync = promisify(execFile);
|
|
25
26
|
|
|
26
27
|
export function registerCommitRoutes(app: Hono) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
openApiRoute(
|
|
29
|
+
app,
|
|
30
|
+
{
|
|
31
|
+
method: 'post',
|
|
32
|
+
path: '/v1/git/commit',
|
|
33
|
+
tags: ['git'],
|
|
34
|
+
operationId: 'commitChanges',
|
|
35
|
+
summary: 'Commit staged changes',
|
|
36
|
+
requestBody: {
|
|
37
|
+
required: true,
|
|
38
|
+
content: {
|
|
39
|
+
'application/json': {
|
|
40
|
+
schema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
project: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
},
|
|
46
|
+
message: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
minLength: 1,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ['message'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
50
54
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
},
|
|
56
|
+
responses: {
|
|
57
|
+
'200': {
|
|
58
|
+
description: 'OK',
|
|
59
|
+
content: {
|
|
60
|
+
'application/json': {
|
|
61
|
+
schema: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
status: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
enum: ['ok'],
|
|
67
|
+
},
|
|
68
|
+
data: {
|
|
69
|
+
$ref: '#/components/schemas/GitCommit',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ['status', 'data'],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
57
76
|
},
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
'400': {
|
|
78
|
+
description: 'Error',
|
|
79
|
+
content: {
|
|
80
|
+
'application/json': {
|
|
81
|
+
schema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
status: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
enum: ['error'],
|
|
87
|
+
},
|
|
88
|
+
error: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
},
|
|
91
|
+
code: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ['status', 'error'],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
64
99
|
},
|
|
65
|
-
500
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
'500': {
|
|
101
|
+
description: 'Error',
|
|
102
|
+
content: {
|
|
103
|
+
'application/json': {
|
|
104
|
+
schema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
status: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
enum: ['error'],
|
|
110
|
+
},
|
|
111
|
+
error: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
},
|
|
114
|
+
code: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
required: ['status', 'error'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
async (c) => {
|
|
126
|
+
try {
|
|
127
|
+
const body = await c.req.json();
|
|
128
|
+
const { message, project } = gitCommitSchema.parse(body);
|
|
129
|
+
|
|
130
|
+
const requestedPath = project || process.cwd();
|
|
131
|
+
|
|
132
|
+
const validation = await validateAndGetGitRoot(requestedPath);
|
|
133
|
+
if ('error' in validation) {
|
|
134
|
+
return c.json(
|
|
135
|
+
{ status: 'error', error: validation.error, code: validation.code },
|
|
136
|
+
400,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
69
139
|
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const body = await c.req.json();
|
|
73
|
-
const { project, sessionId } = gitGenerateCommitMessageSchema.parse(body);
|
|
140
|
+
const { gitRoot } = validation;
|
|
74
141
|
|
|
75
|
-
|
|
142
|
+
const fullMessage = appendCoAuthorTrailer(message);
|
|
143
|
+
const { stdout } = await execFileAsync(
|
|
144
|
+
'git',
|
|
145
|
+
['commit', '-m', fullMessage],
|
|
146
|
+
{
|
|
147
|
+
cwd: gitRoot,
|
|
148
|
+
},
|
|
149
|
+
);
|
|
76
150
|
|
|
77
|
-
|
|
78
|
-
|
|
151
|
+
return c.json({
|
|
152
|
+
status: 'ok',
|
|
153
|
+
data: {
|
|
154
|
+
message: stdout.trim(),
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
79
158
|
return c.json(
|
|
80
|
-
{
|
|
81
|
-
|
|
159
|
+
{
|
|
160
|
+
status: 'error',
|
|
161
|
+
error: error instanceof Error ? error.message : 'Failed to commit',
|
|
162
|
+
},
|
|
163
|
+
500,
|
|
82
164
|
);
|
|
83
165
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
openApiRoute(
|
|
170
|
+
app,
|
|
171
|
+
{
|
|
172
|
+
method: 'post',
|
|
173
|
+
path: '/v1/git/generate-commit-message',
|
|
174
|
+
tags: ['git'],
|
|
175
|
+
operationId: 'generateCommitMessage',
|
|
176
|
+
summary: 'Generate AI-powered commit message',
|
|
177
|
+
description:
|
|
178
|
+
'Uses AI to generate a commit message based on staged changes',
|
|
179
|
+
requestBody: {
|
|
180
|
+
required: false,
|
|
181
|
+
content: {
|
|
182
|
+
'application/json': {
|
|
183
|
+
schema: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
properties: {
|
|
186
|
+
project: {
|
|
187
|
+
type: 'string',
|
|
188
|
+
},
|
|
189
|
+
sessionId: {
|
|
190
|
+
type: 'string',
|
|
191
|
+
description: 'Session ID to use session provider',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
responses: {
|
|
199
|
+
'200': {
|
|
200
|
+
description: 'OK',
|
|
201
|
+
content: {
|
|
202
|
+
'application/json': {
|
|
203
|
+
schema: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
status: {
|
|
207
|
+
type: 'string',
|
|
208
|
+
enum: ['ok'],
|
|
209
|
+
},
|
|
210
|
+
data: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
message: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: ['message'],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
required: ['status', 'data'],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
'400': {
|
|
226
|
+
description: 'Error',
|
|
227
|
+
content: {
|
|
228
|
+
'application/json': {
|
|
229
|
+
schema: {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
status: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
enum: ['error'],
|
|
235
|
+
},
|
|
236
|
+
error: {
|
|
237
|
+
type: 'string',
|
|
238
|
+
},
|
|
239
|
+
code: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
required: ['status', 'error'],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
92
247
|
},
|
|
93
|
-
|
|
248
|
+
'500': {
|
|
249
|
+
description: 'Error',
|
|
250
|
+
content: {
|
|
251
|
+
'application/json': {
|
|
252
|
+
schema: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
status: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
enum: ['error'],
|
|
258
|
+
},
|
|
259
|
+
error: {
|
|
260
|
+
type: 'string',
|
|
261
|
+
},
|
|
262
|
+
code: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
required: ['status', 'error'],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
async (c) => {
|
|
274
|
+
try {
|
|
275
|
+
const body = await c.req.json();
|
|
276
|
+
const { project, sessionId } =
|
|
277
|
+
gitGenerateCommitMessageSchema.parse(body);
|
|
278
|
+
|
|
279
|
+
const requestedPath = project || process.cwd();
|
|
280
|
+
|
|
281
|
+
const validation = await validateAndGetGitRoot(requestedPath);
|
|
282
|
+
if ('error' in validation) {
|
|
283
|
+
return c.json(
|
|
284
|
+
{ status: 'error', error: validation.error, code: validation.code },
|
|
285
|
+
400,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
94
288
|
|
|
95
|
-
|
|
96
|
-
|
|
289
|
+
const { gitRoot } = validation;
|
|
290
|
+
|
|
291
|
+
const { stdout: diff } = await execFileAsync(
|
|
292
|
+
'git',
|
|
293
|
+
['diff', '--cached'],
|
|
97
294
|
{
|
|
98
|
-
|
|
99
|
-
error: 'No staged changes to generate message from',
|
|
295
|
+
cwd: gitRoot,
|
|
100
296
|
},
|
|
101
|
-
400,
|
|
102
297
|
);
|
|
103
|
-
}
|
|
104
298
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const config = await loadConfig();
|
|
114
|
-
|
|
115
|
-
let provider = (config.defaults?.provider || 'anthropic') as ProviderId;
|
|
116
|
-
let currentModel = config.defaults?.model ?? 'claude-3-5-sonnet-20241022';
|
|
117
|
-
|
|
118
|
-
if (sessionId) {
|
|
119
|
-
const db = await getDb();
|
|
120
|
-
const [session] = await db
|
|
121
|
-
.select({ provider: sessions.provider, model: sessions.model })
|
|
122
|
-
.from(sessions)
|
|
123
|
-
.where(eq(sessions.id, sessionId));
|
|
124
|
-
if (session?.provider) {
|
|
125
|
-
provider = session.provider as ProviderId;
|
|
299
|
+
if (!diff.trim()) {
|
|
300
|
+
return c.json(
|
|
301
|
+
{
|
|
302
|
+
status: 'error',
|
|
303
|
+
error: 'No staged changes to generate message from',
|
|
304
|
+
},
|
|
305
|
+
400,
|
|
306
|
+
);
|
|
126
307
|
}
|
|
127
|
-
|
|
128
|
-
|
|
308
|
+
|
|
309
|
+
const { stdout: statusOutput } = await execFileAsync(
|
|
310
|
+
'git',
|
|
311
|
+
['status', '--porcelain=v2'],
|
|
312
|
+
{ cwd: gitRoot },
|
|
313
|
+
);
|
|
314
|
+
const { staged } = parseGitStatus(statusOutput, gitRoot);
|
|
315
|
+
const fileList = staged.map((f) => `${f.status}: ${f.path}`).join('\n');
|
|
316
|
+
|
|
317
|
+
const config = await loadConfig();
|
|
318
|
+
|
|
319
|
+
let provider = (config.defaults?.provider || 'anthropic') as ProviderId;
|
|
320
|
+
let currentModel =
|
|
321
|
+
config.defaults?.model ?? 'claude-3-5-sonnet-20241022';
|
|
322
|
+
|
|
323
|
+
if (sessionId) {
|
|
324
|
+
const db = await getDb();
|
|
325
|
+
const [session] = await db
|
|
326
|
+
.select({ provider: sessions.provider, model: sessions.model })
|
|
327
|
+
.from(sessions)
|
|
328
|
+
.where(eq(sessions.id, sessionId));
|
|
329
|
+
if (session?.provider) {
|
|
330
|
+
provider = session.provider as ProviderId;
|
|
331
|
+
}
|
|
332
|
+
if (session?.model) {
|
|
333
|
+
currentModel = session.model;
|
|
334
|
+
}
|
|
129
335
|
}
|
|
130
|
-
}
|
|
131
336
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
337
|
+
const auth = await getAuth(provider, config.projectRoot);
|
|
338
|
+
const oauth = detectOAuth(provider, auth);
|
|
339
|
+
const providerDefinition = getProviderDefinition(config, provider);
|
|
135
340
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
341
|
+
const modelId =
|
|
342
|
+
providerDefinition?.source === 'custom' ||
|
|
343
|
+
providerDefinition?.compatibility === 'ollama'
|
|
344
|
+
? currentModel
|
|
345
|
+
: (getFastModelForAuth(provider, auth?.type) ?? currentModel);
|
|
346
|
+
const model = await resolveModel(provider, modelId, config);
|
|
142
347
|
|
|
143
|
-
|
|
348
|
+
const userPrompt = `Generate a commit message for these git changes.
|
|
144
349
|
|
|
145
350
|
Staged files:
|
|
146
351
|
${fileList}
|
|
@@ -168,56 +373,57 @@ refactor(auth): return success status from login functions
|
|
|
168
373
|
|
|
169
374
|
Commit message:`;
|
|
170
375
|
|
|
171
|
-
|
|
172
|
-
|
|
376
|
+
const commitInstructions =
|
|
377
|
+
'You are a helpful assistant that generates accurate git commit messages based on the actual diff content.';
|
|
173
378
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
379
|
+
const adapted = adaptSimpleCall(oauth, {
|
|
380
|
+
instructions: commitInstructions,
|
|
381
|
+
userContent: userPrompt,
|
|
382
|
+
maxOutputTokens: 500,
|
|
383
|
+
});
|
|
179
384
|
|
|
180
|
-
|
|
181
|
-
|
|
385
|
+
if (adapted.forceStream) {
|
|
386
|
+
const result = streamText({
|
|
387
|
+
model,
|
|
388
|
+
system: adapted.system,
|
|
389
|
+
messages: adapted.messages,
|
|
390
|
+
providerOptions: adapted.providerOptions,
|
|
391
|
+
});
|
|
392
|
+
let text = '';
|
|
393
|
+
for await (const chunk of result.textStream) {
|
|
394
|
+
text += chunk;
|
|
395
|
+
}
|
|
396
|
+
const message = text.trim();
|
|
397
|
+
return c.json({ status: 'ok', data: { message } });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const { text } = await generateText({
|
|
182
401
|
model,
|
|
183
402
|
system: adapted.system,
|
|
184
403
|
messages: adapted.messages,
|
|
185
|
-
|
|
404
|
+
maxOutputTokens: adapted.maxOutputTokens,
|
|
186
405
|
});
|
|
187
|
-
let text = '';
|
|
188
|
-
for await (const chunk of result.textStream) {
|
|
189
|
-
text += chunk;
|
|
190
|
-
}
|
|
191
|
-
const message = text.trim();
|
|
192
|
-
return c.json({ status: 'ok', data: { message } });
|
|
193
|
-
}
|
|
194
406
|
|
|
195
|
-
|
|
196
|
-
model,
|
|
197
|
-
system: adapted.system,
|
|
198
|
-
messages: adapted.messages,
|
|
199
|
-
maxOutputTokens: adapted.maxOutputTokens,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
const message = text.trim();
|
|
407
|
+
const message = text.trim();
|
|
203
408
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
409
|
+
return c.json({
|
|
410
|
+
status: 'ok',
|
|
411
|
+
data: {
|
|
412
|
+
message,
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return c.json(
|
|
417
|
+
{
|
|
418
|
+
status: 'error',
|
|
419
|
+
error:
|
|
420
|
+
error instanceof Error
|
|
421
|
+
? error.message
|
|
422
|
+
: 'Failed to generate commit message',
|
|
423
|
+
},
|
|
424
|
+
500,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
);
|
|
223
429
|
}
|