@ottocode/server 0.1.260 → 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/ask/service.ts +1 -0
- package/src/runtime/message/compaction-limits.ts +3 -3
- package/src/runtime/provider/reasoning.ts +2 -1
- 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/terminals.ts
CHANGED
|
@@ -4,72 +4,238 @@ import { streamSSE } from 'hono/streaming';
|
|
|
4
4
|
import type { TerminalManager } from '@ottocode/sdk';
|
|
5
5
|
import { logger } from '@ottocode/sdk';
|
|
6
6
|
import { upgradeWebSocket } from '../ws.ts';
|
|
7
|
+
import { openApiRoute } from '../openapi/route.ts';
|
|
7
8
|
|
|
8
9
|
export function registerTerminalsRoutes(
|
|
9
10
|
app: Hono,
|
|
10
11
|
terminalManager: TerminalManager,
|
|
11
12
|
) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
openApiRoute(
|
|
14
|
+
app,
|
|
15
|
+
{
|
|
16
|
+
method: 'get',
|
|
17
|
+
path: '/v1/terminals',
|
|
18
|
+
operationId: 'getTerminals',
|
|
19
|
+
summary: 'List all terminals',
|
|
20
|
+
description: 'Get a list of all active terminal sessions',
|
|
21
|
+
responses: {
|
|
22
|
+
'200': {
|
|
23
|
+
description: 'List of terminals',
|
|
24
|
+
content: {
|
|
25
|
+
'application/json': {
|
|
26
|
+
schema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
terminals: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: {
|
|
32
|
+
$ref: '#/components/schemas/Terminal',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
count: {
|
|
36
|
+
type: 'integer',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
async (c) => {
|
|
46
|
+
const terminals = terminalManager.list();
|
|
47
|
+
return c.json({
|
|
48
|
+
terminals: terminals.map((t) => t.toJSON()),
|
|
49
|
+
count: terminals.length,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
);
|
|
24
53
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
54
|
+
openApiRoute(
|
|
55
|
+
app,
|
|
56
|
+
{
|
|
57
|
+
method: 'post',
|
|
58
|
+
path: '/v1/terminals',
|
|
59
|
+
operationId: 'postTerminals',
|
|
60
|
+
summary: 'Create a new terminal',
|
|
61
|
+
description: 'Spawn a new terminal process',
|
|
62
|
+
requestBody: {
|
|
63
|
+
required: true,
|
|
64
|
+
content: {
|
|
65
|
+
'application/json': {
|
|
66
|
+
schema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
required: ['command', 'purpose'],
|
|
69
|
+
properties: {
|
|
70
|
+
command: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Command to execute',
|
|
73
|
+
},
|
|
74
|
+
args: {
|
|
75
|
+
type: 'array',
|
|
76
|
+
items: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
},
|
|
79
|
+
description: 'Command arguments',
|
|
80
|
+
},
|
|
81
|
+
purpose: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: 'Description of terminal purpose',
|
|
84
|
+
},
|
|
85
|
+
cwd: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
description: 'Working directory',
|
|
88
|
+
},
|
|
89
|
+
title: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: 'Terminal title',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
responses: {
|
|
99
|
+
'200': {
|
|
100
|
+
description: 'Terminal created',
|
|
101
|
+
content: {
|
|
102
|
+
'application/json': {
|
|
103
|
+
schema: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
terminalId: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
},
|
|
109
|
+
pid: {
|
|
110
|
+
type: 'integer',
|
|
111
|
+
},
|
|
112
|
+
purpose: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
},
|
|
115
|
+
command: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
async (c) => {
|
|
126
|
+
try {
|
|
127
|
+
const body = await c.req.json();
|
|
128
|
+
const { command, args, purpose, cwd, title } = body;
|
|
129
|
+
|
|
130
|
+
if (!command || !purpose) {
|
|
131
|
+
return c.json({ error: 'command and purpose are required' }, 400);
|
|
132
|
+
}
|
|
28
133
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
134
|
+
let resolvedCommand = command;
|
|
135
|
+
if (command === 'bash' || command === 'sh' || command === 'shell') {
|
|
136
|
+
resolvedCommand =
|
|
137
|
+
process.platform === 'win32'
|
|
138
|
+
? process.env.COMSPEC || 'cmd.exe'
|
|
139
|
+
: process.env.SHELL || '/bin/bash';
|
|
140
|
+
}
|
|
141
|
+
const resolvedCwd = cwd || process.cwd();
|
|
142
|
+
|
|
143
|
+
const terminal = terminalManager.create({
|
|
144
|
+
command: resolvedCommand,
|
|
145
|
+
args: args || [],
|
|
146
|
+
purpose,
|
|
147
|
+
cwd: resolvedCwd,
|
|
148
|
+
createdBy: 'user',
|
|
149
|
+
title,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return c.json({
|
|
153
|
+
terminalId: terminal.id,
|
|
154
|
+
pid: terminal.pid,
|
|
155
|
+
purpose: terminal.purpose,
|
|
156
|
+
command: terminal.command,
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logger.error('Error creating terminal', error);
|
|
160
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
161
|
+
return c.json({ error: message }, 500);
|
|
35
162
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const terminal = terminalManager.create({
|
|
39
|
-
command: resolvedCommand,
|
|
40
|
-
args: args || [],
|
|
41
|
-
purpose,
|
|
42
|
-
cwd: resolvedCwd,
|
|
43
|
-
createdBy: 'user',
|
|
44
|
-
title,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
return c.json({
|
|
48
|
-
terminalId: terminal.id,
|
|
49
|
-
pid: terminal.pid,
|
|
50
|
-
purpose: terminal.purpose,
|
|
51
|
-
command: terminal.command,
|
|
52
|
-
});
|
|
53
|
-
} catch (error) {
|
|
54
|
-
logger.error('Error creating terminal', error);
|
|
55
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
-
return c.json({ error: message }, 500);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
163
|
+
},
|
|
164
|
+
);
|
|
59
165
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
166
|
+
openApiRoute(
|
|
167
|
+
app,
|
|
168
|
+
{
|
|
169
|
+
method: 'get',
|
|
170
|
+
path: '/v1/terminals/{id}',
|
|
171
|
+
operationId: 'getTerminalsById',
|
|
172
|
+
summary: 'Get terminal details',
|
|
173
|
+
description: 'Get information about a specific terminal',
|
|
174
|
+
parameters: [
|
|
175
|
+
{
|
|
176
|
+
name: 'id',
|
|
177
|
+
in: 'path',
|
|
178
|
+
required: true,
|
|
179
|
+
schema: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
responses: {
|
|
185
|
+
'200': {
|
|
186
|
+
description: 'Terminal details',
|
|
187
|
+
content: {
|
|
188
|
+
'application/json': {
|
|
189
|
+
schema: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {
|
|
192
|
+
terminal: {
|
|
193
|
+
$ref: '#/components/schemas/Terminal',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
'404': {
|
|
201
|
+
description: 'Terminal not found',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
async (c) => {
|
|
206
|
+
const id = c.req.param('id');
|
|
207
|
+
const terminal = terminalManager.get(id);
|
|
63
208
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
209
|
+
if (!terminal) {
|
|
210
|
+
return c.json({ error: 'Terminal not found' }, 404);
|
|
211
|
+
}
|
|
67
212
|
|
|
68
|
-
|
|
69
|
-
|
|
213
|
+
return c.json({ terminal: terminal.toJSON() });
|
|
214
|
+
},
|
|
215
|
+
);
|
|
70
216
|
|
|
71
|
-
|
|
72
|
-
|
|
217
|
+
openApiRoute(
|
|
218
|
+
app,
|
|
219
|
+
{
|
|
220
|
+
method: 'get',
|
|
221
|
+
path: '/v1/terminals/{id}/ws',
|
|
222
|
+
operationId: 'connectTerminalWebSocket',
|
|
223
|
+
summary: 'Connect to terminal WebSocket',
|
|
224
|
+
description:
|
|
225
|
+
'Upgrade to a WebSocket for bidirectional terminal I/O. Generated HTTP clients cannot consume the upgraded connection directly.',
|
|
226
|
+
parameters: [
|
|
227
|
+
{
|
|
228
|
+
name: 'id',
|
|
229
|
+
in: 'path',
|
|
230
|
+
required: true,
|
|
231
|
+
schema: { type: 'string' },
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
responses: {
|
|
235
|
+
'101': { description: 'WebSocket upgrade accepted' },
|
|
236
|
+
'404': { description: 'Terminal not found' },
|
|
237
|
+
},
|
|
238
|
+
},
|
|
73
239
|
upgradeWebSocket((c) => {
|
|
74
240
|
const id = c.req.param('id');
|
|
75
241
|
|
|
@@ -252,66 +418,266 @@ export function registerTerminalsRoutes(
|
|
|
252
418
|
});
|
|
253
419
|
};
|
|
254
420
|
|
|
255
|
-
|
|
256
|
-
|
|
421
|
+
openApiRoute(
|
|
422
|
+
app,
|
|
423
|
+
{
|
|
424
|
+
method: 'get',
|
|
425
|
+
path: '/v1/terminals/{id}/output',
|
|
426
|
+
operationId: 'getTerminalsByIdOutput',
|
|
427
|
+
summary: 'Stream terminal output',
|
|
428
|
+
description: 'Get real-time terminal output via SSE',
|
|
429
|
+
parameters: [
|
|
430
|
+
{
|
|
431
|
+
name: 'id',
|
|
432
|
+
in: 'path',
|
|
433
|
+
required: true,
|
|
434
|
+
schema: {
|
|
435
|
+
type: 'string',
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
responses: {
|
|
440
|
+
'200': {
|
|
441
|
+
description: 'SSE stream of terminal output',
|
|
442
|
+
content: {
|
|
443
|
+
'text/event-stream': {
|
|
444
|
+
schema: {
|
|
445
|
+
type: 'string',
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
handleTerminalOutput,
|
|
453
|
+
);
|
|
454
|
+
openApiRoute(
|
|
455
|
+
app,
|
|
456
|
+
{
|
|
457
|
+
method: 'post',
|
|
458
|
+
path: '/v1/terminals/{id}/output',
|
|
459
|
+
operationId: 'postTerminalsByIdOutput',
|
|
460
|
+
summary: 'Stream terminal output using POST',
|
|
461
|
+
description: 'Compatibility alias for terminal output SSE',
|
|
462
|
+
parameters: [
|
|
463
|
+
{
|
|
464
|
+
name: 'id',
|
|
465
|
+
in: 'path',
|
|
466
|
+
required: true,
|
|
467
|
+
schema: { type: 'string' },
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
responses: {
|
|
471
|
+
'200': {
|
|
472
|
+
description: 'SSE stream of terminal output',
|
|
473
|
+
content: {
|
|
474
|
+
'text/event-stream': {
|
|
475
|
+
schema: { type: 'string' },
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
handleTerminalOutput,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
openApiRoute(
|
|
485
|
+
app,
|
|
486
|
+
{
|
|
487
|
+
method: 'post',
|
|
488
|
+
path: '/v1/terminals/{id}/input',
|
|
489
|
+
operationId: 'postTerminalsByIdInput',
|
|
490
|
+
summary: 'Send input to terminal',
|
|
491
|
+
description: 'Write data to terminal stdin',
|
|
492
|
+
parameters: [
|
|
493
|
+
{
|
|
494
|
+
name: 'id',
|
|
495
|
+
in: 'path',
|
|
496
|
+
required: true,
|
|
497
|
+
schema: {
|
|
498
|
+
type: 'string',
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
requestBody: {
|
|
503
|
+
required: true,
|
|
504
|
+
content: {
|
|
505
|
+
'application/json': {
|
|
506
|
+
schema: {
|
|
507
|
+
type: 'object',
|
|
508
|
+
required: ['input'],
|
|
509
|
+
properties: {
|
|
510
|
+
input: {
|
|
511
|
+
type: 'string',
|
|
512
|
+
description: 'Input to send to terminal',
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
responses: {
|
|
520
|
+
'200': {
|
|
521
|
+
description: 'Input sent',
|
|
522
|
+
content: {
|
|
523
|
+
'application/json': {
|
|
524
|
+
schema: {
|
|
525
|
+
type: 'object',
|
|
526
|
+
properties: {
|
|
527
|
+
success: {
|
|
528
|
+
type: 'boolean',
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
async (c) => {
|
|
538
|
+
const id = c.req.param('id');
|
|
539
|
+
const terminal = terminalManager.get(id);
|
|
257
540
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
541
|
+
if (!terminal) {
|
|
542
|
+
return c.json({ error: 'Terminal not found' }, 404);
|
|
543
|
+
}
|
|
261
544
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
545
|
+
try {
|
|
546
|
+
const body = await c.req.json();
|
|
547
|
+
const { input } = body;
|
|
265
548
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
549
|
+
if (!input) {
|
|
550
|
+
return c.json({ error: 'input is required' }, 400);
|
|
551
|
+
}
|
|
269
552
|
|
|
270
|
-
|
|
271
|
-
return c.json({
|
|
553
|
+
terminal.write(input);
|
|
554
|
+
return c.json({ success: true });
|
|
555
|
+
} catch (error) {
|
|
556
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
557
|
+
return c.json({ error: message }, 500);
|
|
272
558
|
}
|
|
559
|
+
},
|
|
560
|
+
);
|
|
273
561
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
562
|
+
openApiRoute(
|
|
563
|
+
app,
|
|
564
|
+
{
|
|
565
|
+
method: 'delete',
|
|
566
|
+
path: '/v1/terminals/{id}',
|
|
567
|
+
operationId: 'deleteTerminalsById',
|
|
568
|
+
summary: 'Kill terminal',
|
|
569
|
+
description: 'Terminate a running terminal process',
|
|
570
|
+
parameters: [
|
|
571
|
+
{
|
|
572
|
+
name: 'id',
|
|
573
|
+
in: 'path',
|
|
574
|
+
required: true,
|
|
575
|
+
schema: {
|
|
576
|
+
type: 'string',
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
responses: {
|
|
581
|
+
'200': {
|
|
582
|
+
description: 'Terminal killed',
|
|
583
|
+
content: {
|
|
584
|
+
'application/json': {
|
|
585
|
+
schema: {
|
|
586
|
+
type: 'object',
|
|
587
|
+
properties: {
|
|
588
|
+
success: {
|
|
589
|
+
type: 'boolean',
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
async (c) => {
|
|
599
|
+
const id = c.req.param('id');
|
|
281
600
|
|
|
282
|
-
|
|
283
|
-
|
|
601
|
+
try {
|
|
602
|
+
await terminalManager.kill(id);
|
|
603
|
+
return c.json({ success: true });
|
|
604
|
+
} catch (error) {
|
|
605
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
606
|
+
return c.json({ error: message }, 500);
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
);
|
|
284
610
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
611
|
+
openApiRoute(
|
|
612
|
+
app,
|
|
613
|
+
{
|
|
614
|
+
method: 'post',
|
|
615
|
+
path: '/v1/terminals/{id}/resize',
|
|
616
|
+
operationId: 'resizeTerminal',
|
|
617
|
+
summary: 'Resize terminal',
|
|
618
|
+
description: 'Resize the pseudo-terminal dimensions.',
|
|
619
|
+
parameters: [
|
|
620
|
+
{
|
|
621
|
+
name: 'id',
|
|
622
|
+
in: 'path',
|
|
623
|
+
required: true,
|
|
624
|
+
schema: { type: 'string' },
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
requestBody: {
|
|
628
|
+
required: true,
|
|
629
|
+
content: {
|
|
630
|
+
'application/json': {
|
|
631
|
+
schema: {
|
|
632
|
+
type: 'object',
|
|
633
|
+
required: ['cols', 'rows'],
|
|
634
|
+
properties: {
|
|
635
|
+
cols: { type: 'integer', minimum: 1 },
|
|
636
|
+
rows: { type: 'integer', minimum: 1 },
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
responses: {
|
|
643
|
+
'200': {
|
|
644
|
+
description: 'Terminal resized',
|
|
645
|
+
content: {
|
|
646
|
+
'application/json': {
|
|
647
|
+
schema: {
|
|
648
|
+
type: 'object',
|
|
649
|
+
required: ['success'],
|
|
650
|
+
properties: { success: { type: 'boolean' } },
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
'400': { description: 'Invalid terminal size' },
|
|
656
|
+
'404': { description: 'Terminal not found' },
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
async (c) => {
|
|
660
|
+
const id = c.req.param('id');
|
|
661
|
+
const terminal = terminalManager.get(id);
|
|
293
662
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
663
|
+
if (!terminal) {
|
|
664
|
+
return c.json({ error: 'Terminal not found' }, 404);
|
|
665
|
+
}
|
|
297
666
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
667
|
+
try {
|
|
668
|
+
const body = await c.req.json();
|
|
669
|
+
const { cols, rows } = body;
|
|
301
670
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
671
|
+
if (!cols || !rows || cols < 1 || rows < 1) {
|
|
672
|
+
return c.json({ error: 'valid cols and rows are required' }, 400);
|
|
673
|
+
}
|
|
305
674
|
|
|
306
|
-
|
|
307
|
-
return c.json({
|
|
675
|
+
terminal.resize(cols, rows);
|
|
676
|
+
return c.json({ success: true });
|
|
677
|
+
} catch (error) {
|
|
678
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
679
|
+
return c.json({ error: message }, 500);
|
|
308
680
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return c.json({ success: true });
|
|
312
|
-
} catch (error) {
|
|
313
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
314
|
-
return c.json({ error: message }, 500);
|
|
315
|
-
}
|
|
316
|
-
});
|
|
681
|
+
},
|
|
682
|
+
);
|
|
317
683
|
}
|