@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/pull.ts
CHANGED
|
@@ -3,96 +3,198 @@ import { execFile } from 'node:child_process';
|
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { gitPullSchema } from './schemas.ts';
|
|
5
5
|
import { validateAndGetGitRoot } from './utils.ts';
|
|
6
|
+
import { openApiRoute } from '../../openapi/route.ts';
|
|
6
7
|
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
|
|
9
10
|
export function registerPullRoute(app: Hono) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
openApiRoute(
|
|
12
|
+
app,
|
|
13
|
+
{
|
|
14
|
+
method: 'post',
|
|
15
|
+
path: '/v1/git/pull',
|
|
16
|
+
tags: ['git'],
|
|
17
|
+
operationId: 'pullChanges',
|
|
18
|
+
summary: 'Pull changes from remote',
|
|
19
|
+
description: 'Pulls changes from the configured remote repository',
|
|
20
|
+
requestBody: {
|
|
21
|
+
required: false,
|
|
22
|
+
content: {
|
|
23
|
+
'application/json': {
|
|
24
|
+
schema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
project: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
responses: {
|
|
36
|
+
'200': {
|
|
37
|
+
description: 'OK',
|
|
38
|
+
content: {
|
|
39
|
+
'application/json': {
|
|
40
|
+
schema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
status: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['ok'],
|
|
46
|
+
},
|
|
47
|
+
data: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
output: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ['output'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['status', 'data'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
'400': {
|
|
63
|
+
description: 'Error',
|
|
64
|
+
content: {
|
|
65
|
+
'application/json': {
|
|
66
|
+
schema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
status: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
enum: ['error'],
|
|
72
|
+
},
|
|
73
|
+
error: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
},
|
|
76
|
+
code: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ['status', 'error'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
'500': {
|
|
86
|
+
description: 'Error',
|
|
87
|
+
content: {
|
|
88
|
+
'application/json': {
|
|
89
|
+
schema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
status: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
enum: ['error'],
|
|
95
|
+
},
|
|
96
|
+
error: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
},
|
|
99
|
+
code: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ['status', 'error'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
async (c) => {
|
|
13
111
|
try {
|
|
14
|
-
body =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const requestedPath = project || process.cwd();
|
|
22
|
-
|
|
23
|
-
const validation = await validateAndGetGitRoot(requestedPath);
|
|
24
|
-
if ('error' in validation) {
|
|
25
|
-
return c.json(
|
|
26
|
-
{ status: 'error', error: validation.error, code: validation.code },
|
|
27
|
-
400,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
112
|
+
let body = {};
|
|
113
|
+
try {
|
|
114
|
+
body = await c.req.json();
|
|
115
|
+
} catch {
|
|
116
|
+
body = {};
|
|
117
|
+
}
|
|
30
118
|
|
|
31
|
-
|
|
119
|
+
const { project } = gitPullSchema.parse(body);
|
|
32
120
|
|
|
33
|
-
|
|
34
|
-
const result = await execFileAsync('git', ['pull'], {
|
|
35
|
-
cwd: gitRoot,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
return c.json({
|
|
39
|
-
status: 'ok',
|
|
40
|
-
data: {
|
|
41
|
-
output: result.stdout.trim() || result.stderr.trim(),
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
} catch (pullErr: unknown) {
|
|
45
|
-
const error = pullErr as {
|
|
46
|
-
message?: string;
|
|
47
|
-
stderr?: string;
|
|
48
|
-
};
|
|
49
|
-
const errorMessage = error.stderr || error.message || 'Failed to pull';
|
|
121
|
+
const requestedPath = project || process.cwd();
|
|
50
122
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
errorMessage.includes('merge conflict')
|
|
54
|
-
) {
|
|
123
|
+
const validation = await validateAndGetGitRoot(requestedPath);
|
|
124
|
+
if ('error' in validation) {
|
|
55
125
|
return c.json(
|
|
56
|
-
{
|
|
57
|
-
status: 'error',
|
|
58
|
-
error: 'Merge conflicts detected. Resolve conflicts manually',
|
|
59
|
-
details: errorMessage,
|
|
60
|
-
},
|
|
126
|
+
{ status: 'error', error: validation.error, code: validation.code },
|
|
61
127
|
400,
|
|
62
128
|
);
|
|
63
129
|
}
|
|
64
130
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
131
|
+
const { gitRoot } = validation;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = await execFileAsync('git', ['pull'], {
|
|
135
|
+
cwd: gitRoot,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return c.json({
|
|
139
|
+
status: 'ok',
|
|
140
|
+
data: {
|
|
141
|
+
output: result.stdout.trim() || result.stderr.trim(),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
} catch (pullErr: unknown) {
|
|
145
|
+
const error = pullErr as {
|
|
146
|
+
message?: string;
|
|
147
|
+
stderr?: string;
|
|
148
|
+
};
|
|
149
|
+
const errorMessage =
|
|
150
|
+
error.stderr || error.message || 'Failed to pull';
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
errorMessage.includes('CONFLICT') ||
|
|
154
|
+
errorMessage.includes('merge conflict')
|
|
155
|
+
) {
|
|
156
|
+
return c.json(
|
|
157
|
+
{
|
|
158
|
+
status: 'error',
|
|
159
|
+
error: 'Merge conflicts detected. Resolve conflicts manually',
|
|
160
|
+
details: errorMessage,
|
|
161
|
+
},
|
|
162
|
+
400,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
errorMessage.includes('Permission denied') ||
|
|
168
|
+
errorMessage.includes('authentication')
|
|
169
|
+
) {
|
|
170
|
+
return c.json(
|
|
171
|
+
{
|
|
172
|
+
status: 'error',
|
|
173
|
+
error: 'Authentication failed. Check your git credentials',
|
|
174
|
+
details: errorMessage,
|
|
175
|
+
},
|
|
176
|
+
401,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
69
180
|
return c.json(
|
|
70
181
|
{
|
|
71
182
|
status: 'error',
|
|
72
|
-
error: '
|
|
183
|
+
error: 'Failed to pull changes',
|
|
73
184
|
details: errorMessage,
|
|
74
185
|
},
|
|
75
|
-
|
|
186
|
+
500,
|
|
76
187
|
);
|
|
77
188
|
}
|
|
78
|
-
|
|
189
|
+
} catch (error) {
|
|
79
190
|
return c.json(
|
|
80
191
|
{
|
|
81
192
|
status: 'error',
|
|
82
|
-
error: 'Failed to pull
|
|
83
|
-
details: errorMessage,
|
|
193
|
+
error: error instanceof Error ? error.message : 'Failed to pull',
|
|
84
194
|
},
|
|
85
195
|
500,
|
|
86
196
|
);
|
|
87
197
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
status: 'error',
|
|
92
|
-
error: error instanceof Error ? error.message : 'Failed to pull',
|
|
93
|
-
},
|
|
94
|
-
500,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
198
|
+
},
|
|
199
|
+
);
|
|
98
200
|
}
|
package/src/routes/git/push.ts
CHANGED
|
@@ -3,158 +3,263 @@ import { execFile } from 'node:child_process';
|
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { gitPushSchema } from './schemas.ts';
|
|
5
5
|
import { validateAndGetGitRoot, getCurrentBranch } from './utils.ts';
|
|
6
|
+
import { openApiRoute } from '../../openapi/route.ts';
|
|
6
7
|
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
|
|
9
10
|
export function registerPushRoute(app: Hono) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
11
|
+
openApiRoute(
|
|
12
|
+
app,
|
|
13
|
+
{
|
|
14
|
+
method: 'post',
|
|
15
|
+
path: '/v1/git/push',
|
|
16
|
+
tags: ['git'],
|
|
17
|
+
operationId: 'pushCommits',
|
|
18
|
+
summary: 'Push commits to remote',
|
|
19
|
+
description: 'Pushes local commits to the configured remote repository',
|
|
20
|
+
requestBody: {
|
|
21
|
+
required: false,
|
|
22
|
+
content: {
|
|
23
|
+
'application/json': {
|
|
24
|
+
schema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
project: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
responses: {
|
|
36
|
+
'200': {
|
|
37
|
+
description: 'OK',
|
|
38
|
+
content: {
|
|
39
|
+
'application/json': {
|
|
40
|
+
schema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
status: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['ok'],
|
|
46
|
+
},
|
|
47
|
+
data: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
output: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ['output'],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['status', 'data'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
'400': {
|
|
63
|
+
description: 'Error',
|
|
64
|
+
content: {
|
|
65
|
+
'application/json': {
|
|
66
|
+
schema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: {
|
|
69
|
+
status: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
enum: ['error'],
|
|
72
|
+
},
|
|
73
|
+
error: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
},
|
|
76
|
+
code: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ['status', 'error'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
'500': {
|
|
86
|
+
description: 'Error',
|
|
87
|
+
content: {
|
|
88
|
+
'application/json': {
|
|
89
|
+
schema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
status: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
enum: ['error'],
|
|
95
|
+
},
|
|
96
|
+
error: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
},
|
|
99
|
+
code: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ['status', 'error'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
async (c) => {
|
|
36
111
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
112
|
+
let body = {};
|
|
113
|
+
try {
|
|
114
|
+
body = await c.req.json();
|
|
115
|
+
} catch (jsonError) {
|
|
116
|
+
console.warn(
|
|
117
|
+
'Failed to parse JSON body for git push, using empty object:',
|
|
118
|
+
jsonError,
|
|
44
119
|
);
|
|
45
120
|
}
|
|
46
|
-
} catch {
|
|
47
|
-
return c.json(
|
|
48
|
-
{ status: 'error', error: 'No remote repository configured' },
|
|
49
|
-
400,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
121
|
|
|
53
|
-
|
|
54
|
-
let hasUpstream = false;
|
|
55
|
-
try {
|
|
56
|
-
await execFileAsync(
|
|
57
|
-
'git',
|
|
58
|
-
['rev-parse', '--abbrev-ref', '@{upstream}'],
|
|
59
|
-
{
|
|
60
|
-
cwd: gitRoot,
|
|
61
|
-
},
|
|
62
|
-
);
|
|
63
|
-
hasUpstream = true;
|
|
64
|
-
} catch {}
|
|
122
|
+
const { project } = gitPushSchema.parse(body);
|
|
65
123
|
|
|
66
|
-
|
|
67
|
-
let pushOutput: string;
|
|
68
|
-
let pushError: string;
|
|
124
|
+
const requestedPath = project || process.cwd();
|
|
69
125
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const result = await execFileAsync(
|
|
76
|
-
'git',
|
|
77
|
-
['push', '--set-upstream', 'origin', branch],
|
|
78
|
-
{ cwd: gitRoot },
|
|
126
|
+
const validation = await validateAndGetGitRoot(requestedPath);
|
|
127
|
+
if ('error' in validation) {
|
|
128
|
+
return c.json(
|
|
129
|
+
{ status: 'error', error: validation.error, code: validation.code },
|
|
130
|
+
400,
|
|
79
131
|
);
|
|
80
|
-
pushOutput = result.stdout;
|
|
81
|
-
pushError = result.stderr;
|
|
82
132
|
}
|
|
83
133
|
|
|
84
|
-
|
|
85
|
-
status: 'ok',
|
|
86
|
-
data: {
|
|
87
|
-
output: pushOutput.trim() || pushError.trim(),
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
} catch (pushErr: unknown) {
|
|
91
|
-
const error = pushErr as {
|
|
92
|
-
message?: string;
|
|
93
|
-
stderr?: string;
|
|
94
|
-
code?: number;
|
|
95
|
-
};
|
|
96
|
-
const errorMessage = error.stderr || error.message || 'Failed to push';
|
|
134
|
+
const { gitRoot } = validation;
|
|
97
135
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
136
|
+
try {
|
|
137
|
+
const { stdout: remotes } = await execFileAsync('git', ['remote'], {
|
|
138
|
+
cwd: gitRoot,
|
|
139
|
+
});
|
|
140
|
+
if (!remotes.trim()) {
|
|
141
|
+
return c.json(
|
|
142
|
+
{ status: 'error', error: 'No remote repository configured' },
|
|
143
|
+
400,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
102
147
|
return c.json(
|
|
103
|
-
{
|
|
104
|
-
status: 'error',
|
|
105
|
-
error: 'Push rejected. Try pulling changes first with: git pull',
|
|
106
|
-
details: errorMessage,
|
|
107
|
-
},
|
|
148
|
+
{ status: 'error', error: 'No remote repository configured' },
|
|
108
149
|
400,
|
|
109
150
|
);
|
|
110
151
|
}
|
|
111
152
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
const branch = await getCurrentBranch(gitRoot);
|
|
154
|
+
let hasUpstream = false;
|
|
155
|
+
try {
|
|
156
|
+
await execFileAsync(
|
|
157
|
+
'git',
|
|
158
|
+
['rev-parse', '--abbrev-ref', '@{upstream}'],
|
|
118
159
|
{
|
|
119
|
-
|
|
120
|
-
error: 'Authentication failed. Check your git credentials',
|
|
121
|
-
details: errorMessage,
|
|
160
|
+
cwd: gitRoot,
|
|
122
161
|
},
|
|
123
|
-
401,
|
|
124
162
|
);
|
|
125
|
-
|
|
163
|
+
hasUpstream = true;
|
|
164
|
+
} catch {}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
let pushOutput: string;
|
|
168
|
+
let pushError: string;
|
|
169
|
+
|
|
170
|
+
if (hasUpstream) {
|
|
171
|
+
const result = await execFileAsync('git', ['push'], {
|
|
172
|
+
cwd: gitRoot,
|
|
173
|
+
});
|
|
174
|
+
pushOutput = result.stdout;
|
|
175
|
+
pushError = result.stderr;
|
|
176
|
+
} else {
|
|
177
|
+
const result = await execFileAsync(
|
|
178
|
+
'git',
|
|
179
|
+
['push', '--set-upstream', 'origin', branch],
|
|
180
|
+
{ cwd: gitRoot },
|
|
181
|
+
);
|
|
182
|
+
pushOutput = result.stdout;
|
|
183
|
+
pushError = result.stderr;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return c.json({
|
|
187
|
+
status: 'ok',
|
|
188
|
+
data: {
|
|
189
|
+
output: pushOutput.trim() || pushError.trim(),
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
} catch (pushErr: unknown) {
|
|
193
|
+
const error = pushErr as {
|
|
194
|
+
message?: string;
|
|
195
|
+
stderr?: string;
|
|
196
|
+
code?: number;
|
|
197
|
+
};
|
|
198
|
+
const errorMessage =
|
|
199
|
+
error.stderr || error.message || 'Failed to push';
|
|
200
|
+
|
|
201
|
+
if (
|
|
202
|
+
errorMessage.includes('failed to push') ||
|
|
203
|
+
errorMessage.includes('rejected')
|
|
204
|
+
) {
|
|
205
|
+
return c.json(
|
|
206
|
+
{
|
|
207
|
+
status: 'error',
|
|
208
|
+
error:
|
|
209
|
+
'Push rejected. Try pulling changes first with: git pull',
|
|
210
|
+
details: errorMessage,
|
|
211
|
+
},
|
|
212
|
+
400,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (
|
|
217
|
+
errorMessage.includes('Permission denied') ||
|
|
218
|
+
errorMessage.includes('authentication') ||
|
|
219
|
+
errorMessage.includes('could not read')
|
|
220
|
+
) {
|
|
221
|
+
return c.json(
|
|
222
|
+
{
|
|
223
|
+
status: 'error',
|
|
224
|
+
error: 'Authentication failed. Check your git credentials',
|
|
225
|
+
details: errorMessage,
|
|
226
|
+
},
|
|
227
|
+
401,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (
|
|
232
|
+
errorMessage.includes('Could not resolve host') ||
|
|
233
|
+
errorMessage.includes('network')
|
|
234
|
+
) {
|
|
235
|
+
return c.json(
|
|
236
|
+
{
|
|
237
|
+
status: 'error',
|
|
238
|
+
error: 'Network error. Check your internet connection',
|
|
239
|
+
details: errorMessage,
|
|
240
|
+
},
|
|
241
|
+
503,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
126
244
|
|
|
127
|
-
if (
|
|
128
|
-
errorMessage.includes('Could not resolve host') ||
|
|
129
|
-
errorMessage.includes('network')
|
|
130
|
-
) {
|
|
131
245
|
return c.json(
|
|
132
246
|
{
|
|
133
247
|
status: 'error',
|
|
134
|
-
error: '
|
|
248
|
+
error: 'Failed to push commits',
|
|
135
249
|
details: errorMessage,
|
|
136
250
|
},
|
|
137
|
-
|
|
251
|
+
500,
|
|
138
252
|
);
|
|
139
253
|
}
|
|
140
|
-
|
|
254
|
+
} catch (error) {
|
|
141
255
|
return c.json(
|
|
142
256
|
{
|
|
143
257
|
status: 'error',
|
|
144
|
-
error: 'Failed to push
|
|
145
|
-
details: errorMessage,
|
|
258
|
+
error: error instanceof Error ? error.message : 'Failed to push',
|
|
146
259
|
},
|
|
147
260
|
500,
|
|
148
261
|
);
|
|
149
262
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
status: 'error',
|
|
154
|
-
error: error instanceof Error ? error.message : 'Failed to push',
|
|
155
|
-
},
|
|
156
|
-
500,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
263
|
+
},
|
|
264
|
+
);
|
|
160
265
|
}
|