@ottocode/server 0.1.265 → 0.1.267

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 (74) hide show
  1. package/package.json +3 -3
  2. package/src/routes/auth/copilot.ts +699 -0
  3. package/src/routes/auth/oauth.ts +578 -0
  4. package/src/routes/auth/onboarding.ts +45 -0
  5. package/src/routes/auth/providers.ts +189 -0
  6. package/src/routes/auth/service.ts +167 -0
  7. package/src/routes/auth/state.ts +23 -0
  8. package/src/routes/auth/status.ts +203 -0
  9. package/src/routes/auth/wallet.ts +229 -0
  10. package/src/routes/auth.ts +12 -2080
  11. package/src/routes/config/models-service.ts +411 -0
  12. package/src/routes/config/models.ts +6 -426
  13. package/src/routes/config/providers-service.ts +237 -0
  14. package/src/routes/config/providers.ts +10 -242
  15. package/src/routes/files/handlers.ts +297 -0
  16. package/src/routes/files/service.ts +313 -0
  17. package/src/routes/files.ts +12 -608
  18. package/src/routes/git/commit-service.ts +207 -0
  19. package/src/routes/git/commit.ts +6 -220
  20. package/src/routes/git/remote-service.ts +116 -0
  21. package/src/routes/git/remote.ts +8 -115
  22. package/src/routes/git/staging-service.ts +111 -0
  23. package/src/routes/git/staging.ts +10 -205
  24. package/src/routes/mcp/auth.ts +338 -0
  25. package/src/routes/mcp/lifecycle.ts +263 -0
  26. package/src/routes/mcp/servers.ts +212 -0
  27. package/src/routes/mcp/service.ts +664 -0
  28. package/src/routes/mcp/state.ts +13 -0
  29. package/src/routes/mcp.ts +6 -1233
  30. package/src/routes/ottorouter/billing.ts +593 -0
  31. package/src/routes/ottorouter/service.ts +92 -0
  32. package/src/routes/ottorouter/topup.ts +301 -0
  33. package/src/routes/ottorouter/wallet.ts +370 -0
  34. package/src/routes/ottorouter.ts +6 -1319
  35. package/src/routes/research/service.ts +339 -0
  36. package/src/routes/research.ts +12 -390
  37. package/src/routes/sessions/crud.ts +563 -0
  38. package/src/routes/sessions/queue.ts +242 -0
  39. package/src/routes/sessions/retry.ts +121 -0
  40. package/src/routes/sessions/service.ts +768 -0
  41. package/src/routes/sessions/share.ts +434 -0
  42. package/src/routes/sessions.ts +8 -1977
  43. package/src/routes/skills/service.ts +221 -0
  44. package/src/routes/skills/spec.ts +309 -0
  45. package/src/routes/skills.ts +31 -909
  46. package/src/routes/terminals/service.ts +326 -0
  47. package/src/routes/terminals.ts +19 -295
  48. package/src/routes/tunnel/service.ts +217 -0
  49. package/src/routes/tunnel.ts +29 -219
  50. package/src/runtime/agent/registry-prompts.ts +147 -0
  51. package/src/runtime/agent/registry.ts +6 -124
  52. package/src/runtime/agent/runner-errors.ts +116 -0
  53. package/src/runtime/agent/runner-reminders.ts +45 -0
  54. package/src/runtime/agent/runner-setup-model.ts +75 -0
  55. package/src/runtime/agent/runner-setup-prompt.ts +185 -0
  56. package/src/runtime/agent/runner-setup-tools.ts +103 -0
  57. package/src/runtime/agent/runner-setup-utils.ts +21 -0
  58. package/src/runtime/agent/runner-setup.ts +54 -288
  59. package/src/runtime/agent/runner-telemetry.ts +112 -0
  60. package/src/runtime/agent/runner-text.ts +108 -0
  61. package/src/runtime/agent/runner-tool-observer.ts +86 -0
  62. package/src/runtime/agent/runner.ts +79 -378
  63. package/src/runtime/prompt/builder.ts +5 -1
  64. package/src/runtime/prompt/capabilities.ts +13 -8
  65. package/src/runtime/provider/custom.ts +73 -0
  66. package/src/runtime/provider/index.ts +2 -85
  67. package/src/runtime/provider/reasoning-builders.ts +280 -0
  68. package/src/runtime/provider/reasoning.ts +67 -264
  69. package/src/tools/adapter/events.ts +116 -0
  70. package/src/tools/adapter/execution.ts +160 -0
  71. package/src/tools/adapter/pending.ts +37 -0
  72. package/src/tools/adapter/persistence.ts +166 -0
  73. package/src/tools/adapter/results.ts +97 -0
  74. package/src/tools/adapter.ts +124 -451
@@ -0,0 +1,301 @@
1
+ import type { Hono } from 'hono';
2
+ import { logger } from '@ottocode/sdk';
3
+ import { publish } from '../../events/bus.ts';
4
+ import { openApiRoute } from '../../openapi/route.ts';
5
+ import { serializeError } from '../../runtime/errors/api-error.ts';
6
+ import {
7
+ getPendingTopup,
8
+ rejectTopupSelection,
9
+ resolveTopupMethodSelection,
10
+ type TopupMethod,
11
+ } from '../../runtime/topup/manager.ts';
12
+
13
+ export function registerOttoRouterTopupRoutes(app: Hono) {
14
+ openApiRoute(
15
+ app,
16
+ {
17
+ method: 'post',
18
+ path: '/v1/ottorouter/topup/select',
19
+ tags: ['ottorouter'],
20
+ operationId: 'selectTopupMethod',
21
+ summary: 'Select topup method for pending request',
22
+ requestBody: {
23
+ required: true,
24
+ content: {
25
+ 'application/json': {
26
+ schema: {
27
+ type: 'object',
28
+ properties: {
29
+ sessionId: {
30
+ type: 'string',
31
+ },
32
+ method: {
33
+ type: 'string',
34
+ enum: ['crypto', 'fiat'],
35
+ },
36
+ },
37
+ required: ['sessionId', 'method'],
38
+ },
39
+ },
40
+ },
41
+ },
42
+ responses: {
43
+ '200': {
44
+ description: 'OK',
45
+ content: {
46
+ 'application/json': {
47
+ schema: {
48
+ type: 'object',
49
+ properties: {
50
+ success: {
51
+ type: 'boolean',
52
+ },
53
+ method: {
54
+ type: 'string',
55
+ },
56
+ },
57
+ required: ['success', 'method'],
58
+ },
59
+ },
60
+ },
61
+ },
62
+ '404': {
63
+ description: 'No pending topup',
64
+ content: {
65
+ 'application/json': {
66
+ schema: {
67
+ type: 'object',
68
+ properties: {
69
+ error: {
70
+ type: 'string',
71
+ },
72
+ },
73
+ required: ['error'],
74
+ },
75
+ },
76
+ },
77
+ },
78
+ },
79
+ },
80
+ async (c) => {
81
+ try {
82
+ const body = await c.req.json();
83
+ const { sessionId, method } = body as {
84
+ sessionId: string;
85
+ method: TopupMethod;
86
+ };
87
+
88
+ if (!sessionId || typeof sessionId !== 'string') {
89
+ return c.json({ error: 'Missing sessionId' }, 400);
90
+ }
91
+
92
+ if (!method || !['crypto', 'fiat'].includes(method)) {
93
+ return c.json(
94
+ { error: 'Invalid method, must be "crypto" or "fiat"' },
95
+ 400,
96
+ );
97
+ }
98
+
99
+ const resolved = resolveTopupMethodSelection(sessionId, method);
100
+ if (!resolved) {
101
+ return c.json(
102
+ { error: 'No pending topup request found for this session' },
103
+ 404,
104
+ );
105
+ }
106
+
107
+ publish({
108
+ type: 'ottorouter.topup.method_selected',
109
+ sessionId,
110
+ payload: { method },
111
+ });
112
+
113
+ return c.json({ success: true, method });
114
+ } catch (error) {
115
+ logger.error('Failed to select topup method', error);
116
+ const errorResponse = serializeError(error);
117
+ return c.json(errorResponse, errorResponse.error.status || 500);
118
+ }
119
+ },
120
+ );
121
+
122
+ openApiRoute(
123
+ app,
124
+ {
125
+ method: 'post',
126
+ path: '/v1/ottorouter/topup/cancel',
127
+ tags: ['ottorouter'],
128
+ operationId: 'cancelTopup',
129
+ summary: 'Cancel pending topup',
130
+ requestBody: {
131
+ required: true,
132
+ content: {
133
+ 'application/json': {
134
+ schema: {
135
+ type: 'object',
136
+ properties: {
137
+ sessionId: {
138
+ type: 'string',
139
+ },
140
+ reason: {
141
+ type: 'string',
142
+ },
143
+ },
144
+ required: ['sessionId'],
145
+ },
146
+ },
147
+ },
148
+ },
149
+ responses: {
150
+ '200': {
151
+ description: 'OK',
152
+ content: {
153
+ 'application/json': {
154
+ schema: {
155
+ type: 'object',
156
+ properties: {
157
+ success: {
158
+ type: 'boolean',
159
+ },
160
+ },
161
+ required: ['success'],
162
+ },
163
+ },
164
+ },
165
+ },
166
+ '404': {
167
+ description: 'No pending topup',
168
+ content: {
169
+ 'application/json': {
170
+ schema: {
171
+ type: 'object',
172
+ properties: {
173
+ error: {
174
+ type: 'string',
175
+ },
176
+ },
177
+ required: ['error'],
178
+ },
179
+ },
180
+ },
181
+ },
182
+ },
183
+ },
184
+ async (c) => {
185
+ try {
186
+ const body = await c.req.json();
187
+ const { sessionId, reason } = body as {
188
+ sessionId: string;
189
+ reason?: string;
190
+ };
191
+
192
+ if (!sessionId || typeof sessionId !== 'string') {
193
+ return c.json({ error: 'Missing sessionId' }, 400);
194
+ }
195
+
196
+ const rejected = rejectTopupSelection(
197
+ sessionId,
198
+ reason ?? 'User cancelled',
199
+ );
200
+ if (!rejected) {
201
+ return c.json(
202
+ { error: 'No pending topup request found for this session' },
203
+ 404,
204
+ );
205
+ }
206
+
207
+ publish({
208
+ type: 'ottorouter.topup.cancelled',
209
+ sessionId,
210
+ payload: { reason: reason ?? 'User cancelled' },
211
+ });
212
+
213
+ return c.json({ success: true });
214
+ } catch (error) {
215
+ logger.error('Failed to cancel topup', error);
216
+ const errorResponse = serializeError(error);
217
+ return c.json(errorResponse, errorResponse.error.status || 500);
218
+ }
219
+ },
220
+ );
221
+
222
+ openApiRoute(
223
+ app,
224
+ {
225
+ method: 'get',
226
+ path: '/v1/ottorouter/topup/pending',
227
+ tags: ['ottorouter'],
228
+ operationId: 'getPendingTopup',
229
+ summary: 'Get pending topup for a session',
230
+ parameters: [
231
+ {
232
+ in: 'query',
233
+ name: 'sessionId',
234
+ required: true,
235
+ schema: {
236
+ type: 'string',
237
+ },
238
+ },
239
+ ],
240
+ responses: {
241
+ '200': {
242
+ description: 'OK',
243
+ content: {
244
+ 'application/json': {
245
+ schema: {
246
+ type: 'object',
247
+ properties: {
248
+ hasPending: {
249
+ type: 'boolean',
250
+ },
251
+ sessionId: {
252
+ type: 'string',
253
+ },
254
+ messageId: {
255
+ type: 'string',
256
+ },
257
+ amountUsd: {
258
+ type: 'number',
259
+ },
260
+ currentBalance: {
261
+ type: 'number',
262
+ },
263
+ createdAt: {
264
+ type: 'integer',
265
+ },
266
+ },
267
+ required: ['hasPending'],
268
+ },
269
+ },
270
+ },
271
+ },
272
+ },
273
+ },
274
+ async (c) => {
275
+ try {
276
+ const sessionId = c.req.query('sessionId');
277
+ if (!sessionId) {
278
+ return c.json({ error: 'Missing sessionId parameter' }, 400);
279
+ }
280
+
281
+ const pending = getPendingTopup(sessionId);
282
+ if (!pending) {
283
+ return c.json({ hasPending: false });
284
+ }
285
+
286
+ return c.json({
287
+ hasPending: true,
288
+ sessionId: pending.sessionId,
289
+ messageId: pending.messageId,
290
+ amountUsd: pending.amountUsd,
291
+ currentBalance: pending.currentBalance,
292
+ createdAt: pending.createdAt,
293
+ });
294
+ } catch (error) {
295
+ logger.error('Failed to get pending topup', error);
296
+ const errorResponse = serializeError(error);
297
+ return c.json(errorResponse, errorResponse.error.status || 500);
298
+ }
299
+ },
300
+ );
301
+ }
@@ -0,0 +1,370 @@
1
+ import type { Hono } from 'hono';
2
+ import { getPublicKeyFromPrivate, logger } from '@ottocode/sdk';
3
+ import { openApiRoute } from '../../openapi/route.ts';
4
+ import { serializeError } from '../../runtime/errors/api-error.ts';
5
+ import {
6
+ getOttoRouterBalance,
7
+ getOttoRouterBaseUrl,
8
+ getOttoRouterPrivateKey,
9
+ getOttoRouterWalletInfo,
10
+ } from './service.ts';
11
+
12
+ export function registerOttoRouterWalletRoutes(app: Hono) {
13
+ openApiRoute(
14
+ app,
15
+ {
16
+ method: 'get',
17
+ path: '/v1/ottorouter/balance',
18
+ tags: ['ottorouter'],
19
+ operationId: 'getOttoRouterBalance',
20
+ summary: 'Get OttoRouter account balance',
21
+ description:
22
+ 'Returns wallet balance, subscription, account info, limits, and usage data',
23
+ responses: {
24
+ '200': {
25
+ description: 'OK',
26
+ content: {
27
+ 'application/json': {
28
+ schema: {
29
+ type: 'object',
30
+ properties: {
31
+ walletAddress: {
32
+ type: 'string',
33
+ },
34
+ balance: {
35
+ type: 'number',
36
+ },
37
+ totalSpent: {
38
+ type: 'number',
39
+ },
40
+ totalTopups: {
41
+ type: 'number',
42
+ },
43
+ requestCount: {
44
+ type: 'number',
45
+ },
46
+ scope: {
47
+ type: 'string',
48
+ enum: ['wallet', 'account'],
49
+ },
50
+ payg: {
51
+ type: 'object',
52
+ properties: {
53
+ walletBalanceUsd: {
54
+ type: 'number',
55
+ },
56
+ accountBalanceUsd: {
57
+ type: 'number',
58
+ },
59
+ rawPoolUsd: {
60
+ type: 'number',
61
+ },
62
+ effectiveSpendableUsd: {
63
+ type: 'number',
64
+ },
65
+ },
66
+ },
67
+ limits: {
68
+ type: 'object',
69
+ nullable: true,
70
+ properties: {
71
+ enabled: {
72
+ type: 'boolean',
73
+ },
74
+ dailyLimitUsd: {
75
+ type: 'number',
76
+ nullable: true,
77
+ },
78
+ dailySpentUsd: {
79
+ type: 'number',
80
+ },
81
+ dailyRemainingUsd: {
82
+ type: 'number',
83
+ nullable: true,
84
+ },
85
+ monthlyLimitUsd: {
86
+ type: 'number',
87
+ nullable: true,
88
+ },
89
+ monthlySpentUsd: {
90
+ type: 'number',
91
+ },
92
+ monthlyRemainingUsd: {
93
+ type: 'number',
94
+ nullable: true,
95
+ },
96
+ capRemainingUsd: {
97
+ type: 'number',
98
+ nullable: true,
99
+ },
100
+ },
101
+ },
102
+ subscription: {
103
+ type: 'object',
104
+ nullable: true,
105
+ properties: {
106
+ active: {
107
+ type: 'boolean',
108
+ },
109
+ tierId: {
110
+ type: 'string',
111
+ },
112
+ tierName: {
113
+ type: 'string',
114
+ },
115
+ creditsIncluded: {
116
+ type: 'number',
117
+ },
118
+ creditsUsed: {
119
+ type: 'number',
120
+ },
121
+ creditsRemaining: {
122
+ type: 'number',
123
+ },
124
+ periodStart: {
125
+ type: 'string',
126
+ },
127
+ periodEnd: {
128
+ type: 'string',
129
+ },
130
+ },
131
+ },
132
+ },
133
+ required: [
134
+ 'walletAddress',
135
+ 'balance',
136
+ 'totalSpent',
137
+ 'totalTopups',
138
+ 'requestCount',
139
+ ],
140
+ },
141
+ },
142
+ },
143
+ },
144
+ '401': {
145
+ description: 'Wallet not configured',
146
+ content: {
147
+ 'application/json': {
148
+ schema: {
149
+ type: 'object',
150
+ properties: {
151
+ error: {
152
+ type: 'string',
153
+ },
154
+ },
155
+ required: ['error'],
156
+ },
157
+ },
158
+ },
159
+ },
160
+ '502': {
161
+ description: 'Failed to fetch balance from OttoRouter',
162
+ content: {
163
+ 'application/json': {
164
+ schema: {
165
+ type: 'object',
166
+ properties: {
167
+ error: {
168
+ type: 'string',
169
+ },
170
+ },
171
+ required: ['error'],
172
+ },
173
+ },
174
+ },
175
+ },
176
+ },
177
+ },
178
+ async (c) => {
179
+ try {
180
+ const result = await getOttoRouterBalance();
181
+ return result.ok
182
+ ? c.json(result.body)
183
+ : c.json(result.body, result.status);
184
+ } catch (error) {
185
+ logger.error('Failed to fetch OttoRouter balance', error);
186
+ const errorResponse = serializeError(error);
187
+ return c.json(errorResponse, errorResponse.error.status || 500);
188
+ }
189
+ },
190
+ );
191
+
192
+ openApiRoute(
193
+ app,
194
+ {
195
+ method: 'get',
196
+ path: '/v1/ottorouter/wallet',
197
+ tags: ['ottorouter'],
198
+ operationId: 'getOttoRouterWallet',
199
+ summary: 'Get OttoRouter wallet info',
200
+ description:
201
+ 'Returns whether the wallet is configured and its public key',
202
+ responses: {
203
+ '200': {
204
+ description: 'OK',
205
+ content: {
206
+ 'application/json': {
207
+ schema: {
208
+ type: 'object',
209
+ properties: {
210
+ configured: {
211
+ type: 'boolean',
212
+ },
213
+ publicKey: {
214
+ type: 'string',
215
+ },
216
+ error: {
217
+ type: 'string',
218
+ },
219
+ },
220
+ required: ['configured'],
221
+ },
222
+ },
223
+ },
224
+ },
225
+ },
226
+ },
227
+ async (c) => {
228
+ try {
229
+ return c.json(await getOttoRouterWalletInfo());
230
+ } catch (error) {
231
+ logger.error('Failed to get OttoRouter wallet info', error);
232
+ const errorResponse = serializeError(error);
233
+ return c.json(errorResponse, errorResponse.error.status || 500);
234
+ }
235
+ },
236
+ );
237
+
238
+ openApiRoute(
239
+ app,
240
+ {
241
+ method: 'get',
242
+ path: '/v1/ottorouter/usdc-balance',
243
+ tags: ['ottorouter'],
244
+ operationId: 'getOttoRouterUsdcBalance',
245
+ summary: 'Get USDC token balance',
246
+ description:
247
+ 'Fetches USDC balance from Solana blockchain for the configured wallet',
248
+ parameters: [
249
+ {
250
+ in: 'query',
251
+ name: 'network',
252
+ schema: {
253
+ type: 'string',
254
+ enum: ['mainnet', 'devnet'],
255
+ default: 'mainnet',
256
+ },
257
+ description: 'Solana network to query',
258
+ },
259
+ ],
260
+ responses: {
261
+ '200': {
262
+ description: 'OK',
263
+ content: {
264
+ 'application/json': {
265
+ schema: {
266
+ type: 'object',
267
+ properties: {
268
+ walletAddress: {
269
+ type: 'string',
270
+ },
271
+ usdcBalance: {
272
+ type: 'number',
273
+ },
274
+ network: {
275
+ type: 'string',
276
+ enum: ['mainnet', 'devnet'],
277
+ },
278
+ },
279
+ required: ['walletAddress', 'usdcBalance', 'network'],
280
+ },
281
+ },
282
+ },
283
+ },
284
+ '401': {
285
+ description: 'Wallet not configured',
286
+ content: {
287
+ 'application/json': {
288
+ schema: {
289
+ type: 'object',
290
+ properties: {
291
+ error: {
292
+ type: 'string',
293
+ },
294
+ },
295
+ required: ['error'],
296
+ },
297
+ },
298
+ },
299
+ },
300
+ '502': {
301
+ description: 'Failed to fetch USDC balance from Solana',
302
+ content: {
303
+ 'application/json': {
304
+ schema: {
305
+ type: 'object',
306
+ properties: {
307
+ error: {
308
+ type: 'string',
309
+ },
310
+ },
311
+ required: ['error'],
312
+ },
313
+ },
314
+ },
315
+ },
316
+ },
317
+ },
318
+ async (c) => {
319
+ try {
320
+ const privateKey = await getOttoRouterPrivateKey();
321
+ if (!privateKey) {
322
+ return c.json({ error: 'OttoRouter wallet not configured' }, 401);
323
+ }
324
+
325
+ const publicKey = getPublicKeyFromPrivate(privateKey);
326
+ if (!publicKey) {
327
+ return c.json({ error: 'Invalid private key' }, 400);
328
+ }
329
+
330
+ const baseUrl = getOttoRouterBaseUrl();
331
+ const response = await fetch(
332
+ `${baseUrl}/v1/wallet/${publicKey}/balances?limit=100&showNative=false&showNfts=false&showZeroBalance=false`,
333
+ {
334
+ method: 'GET',
335
+ headers: { 'Content-Type': 'application/json' },
336
+ },
337
+ );
338
+
339
+ if (!response.ok) {
340
+ return c.json({ error: 'Failed to fetch wallet balances' }, 502);
341
+ }
342
+
343
+ const data = (await response.json()) as {
344
+ balances: Array<{
345
+ mint: string;
346
+ symbol: string;
347
+ name: string;
348
+ balance: number;
349
+ decimals: number;
350
+ pricePerToken: number | null;
351
+ usdValue: number | null;
352
+ }>;
353
+ totalUsdValue: number;
354
+ };
355
+
356
+ const usdcEntry = data.balances.find((b) => b.symbol === 'USDC');
357
+
358
+ return c.json({
359
+ walletAddress: publicKey,
360
+ usdcBalance: usdcEntry?.balance ?? 0,
361
+ network: 'mainnet' as const,
362
+ });
363
+ } catch (error) {
364
+ logger.error('Failed to fetch USDC balance', error);
365
+ const errorResponse = serializeError(error);
366
+ return c.json(errorResponse, errorResponse.error.status || 500);
367
+ }
368
+ },
369
+ );
370
+ }