@ottocode/server 0.1.243 → 0.1.245
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 +3 -3
- package/src/events/types.ts +9 -9
- package/src/index.ts +4 -4
- package/src/openapi/paths/auth.ts +11 -11
- package/src/openapi/paths/{setu.ts → ottorouter.ts} +31 -31
- package/src/openapi/spec.ts +3 -3
- package/src/routes/auth.ts +16 -16
- package/src/routes/doctor.ts +2 -2
- package/src/routes/{setu.ts → ottorouter.ts} +52 -49
- package/src/runtime/ask/service.ts +1 -1
- package/src/runtime/provider/index.ts +8 -5
- package/src/runtime/provider/{setu.ts → ottorouter.ts} +22 -22
- package/src/runtime/provider/selection.ts +1 -1
- package/src/runtime/session/db-operations.ts +1 -1
- package/src/runtime/stream/error-handler.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.245",
|
|
4
4
|
"description": "HTTP API server for ottocode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typecheck": "tsc --noEmit"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@ottocode/sdk": "0.1.
|
|
53
|
-
"@ottocode/database": "0.1.
|
|
52
|
+
"@ottocode/sdk": "0.1.245",
|
|
53
|
+
"@ottocode/database": "0.1.245",
|
|
54
54
|
"drizzle-orm": "^0.44.5",
|
|
55
55
|
"hono": "^4.9.9",
|
|
56
56
|
"zod": "^4.3.6"
|
package/src/events/types.ts
CHANGED
|
@@ -2,15 +2,15 @@ export type OttoEventType =
|
|
|
2
2
|
| 'tool.approval.required'
|
|
3
3
|
| 'tool.approval.updated'
|
|
4
4
|
| 'tool.approval.resolved'
|
|
5
|
-
| '
|
|
6
|
-
| '
|
|
7
|
-
| '
|
|
8
|
-
| '
|
|
9
|
-
| '
|
|
10
|
-
| '
|
|
11
|
-
| '
|
|
12
|
-
| '
|
|
13
|
-
| '
|
|
5
|
+
| 'ottorouter.payment.required'
|
|
6
|
+
| 'ottorouter.payment.signing'
|
|
7
|
+
| 'ottorouter.payment.complete'
|
|
8
|
+
| 'ottorouter.payment.error'
|
|
9
|
+
| 'ottorouter.topup.required'
|
|
10
|
+
| 'ottorouter.topup.method_selected'
|
|
11
|
+
| 'ottorouter.topup.cancelled'
|
|
12
|
+
| 'ottorouter.fiat.checkout_created'
|
|
13
|
+
| 'ottorouter.balance.updated'
|
|
14
14
|
| 'session.created'
|
|
15
15
|
| 'session.deleted'
|
|
16
16
|
| 'session.updated'
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { registerSessionFilesRoutes } from './routes/session-files.ts';
|
|
|
17
17
|
import { registerBranchRoutes } from './routes/branch.ts';
|
|
18
18
|
import { registerResearchRoutes } from './routes/research.ts';
|
|
19
19
|
import { registerSessionApprovalRoute } from './routes/session-approval.ts';
|
|
20
|
-
import {
|
|
20
|
+
import { registerOttoRouterRoutes } from './routes/ottorouter.ts';
|
|
21
21
|
import { registerAuthRoutes } from './routes/auth.ts';
|
|
22
22
|
import { registerTunnelRoutes } from './routes/tunnel.ts';
|
|
23
23
|
import { registerMCPRoutes } from './routes/mcp.ts';
|
|
@@ -81,7 +81,7 @@ function initApp() {
|
|
|
81
81
|
registerSessionFilesRoutes(app);
|
|
82
82
|
registerBranchRoutes(app);
|
|
83
83
|
registerResearchRoutes(app);
|
|
84
|
-
|
|
84
|
+
registerOttoRouterRoutes(app);
|
|
85
85
|
registerAuthRoutes(app);
|
|
86
86
|
registerTunnelRoutes(app);
|
|
87
87
|
registerMCPRoutes(app);
|
|
@@ -156,7 +156,7 @@ export function createStandaloneApp(_config?: StandaloneAppConfig) {
|
|
|
156
156
|
registerSessionFilesRoutes(honoApp);
|
|
157
157
|
registerBranchRoutes(honoApp);
|
|
158
158
|
registerResearchRoutes(honoApp);
|
|
159
|
-
|
|
159
|
+
registerOttoRouterRoutes(honoApp);
|
|
160
160
|
registerAuthRoutes(honoApp);
|
|
161
161
|
registerTunnelRoutes(honoApp);
|
|
162
162
|
registerMCPRoutes(honoApp);
|
|
@@ -267,7 +267,7 @@ export function createEmbeddedApp(config: EmbeddedAppConfig = {}) {
|
|
|
267
267
|
registerSessionFilesRoutes(honoApp);
|
|
268
268
|
registerBranchRoutes(honoApp);
|
|
269
269
|
registerResearchRoutes(honoApp);
|
|
270
|
-
|
|
270
|
+
registerOttoRouterRoutes(honoApp);
|
|
271
271
|
registerAuthRoutes(honoApp);
|
|
272
272
|
registerTunnelRoutes(honoApp);
|
|
273
273
|
registerMCPRoutes(honoApp);
|
|
@@ -15,7 +15,7 @@ export const authPaths = {
|
|
|
15
15
|
type: 'object',
|
|
16
16
|
properties: {
|
|
17
17
|
onboardingComplete: { type: 'boolean' },
|
|
18
|
-
|
|
18
|
+
ottorouter: {
|
|
19
19
|
type: 'object',
|
|
20
20
|
properties: {
|
|
21
21
|
configured: { type: 'boolean' },
|
|
@@ -65,7 +65,7 @@ export const authPaths = {
|
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
67
|
},
|
|
68
|
-
required: ['onboardingComplete', '
|
|
68
|
+
required: ['onboardingComplete', 'ottorouter', 'providers'],
|
|
69
69
|
},
|
|
70
70
|
},
|
|
71
71
|
},
|
|
@@ -73,11 +73,11 @@ export const authPaths = {
|
|
|
73
73
|
},
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
|
-
'/v1/auth/
|
|
76
|
+
'/v1/auth/ottorouter/setup': {
|
|
77
77
|
post: {
|
|
78
78
|
tags: ['auth'],
|
|
79
|
-
operationId: '
|
|
80
|
-
summary: 'Setup or ensure
|
|
79
|
+
operationId: 'setupOttoRouterWallet',
|
|
80
|
+
summary: 'Setup or ensure OttoRouter wallet',
|
|
81
81
|
responses: {
|
|
82
82
|
200: {
|
|
83
83
|
description: 'OK',
|
|
@@ -98,11 +98,11 @@ export const authPaths = {
|
|
|
98
98
|
},
|
|
99
99
|
},
|
|
100
100
|
},
|
|
101
|
-
'/v1/auth/
|
|
101
|
+
'/v1/auth/ottorouter/import': {
|
|
102
102
|
post: {
|
|
103
103
|
tags: ['auth'],
|
|
104
|
-
operationId: '
|
|
105
|
-
summary: 'Import
|
|
104
|
+
operationId: 'importOttoRouterWallet',
|
|
105
|
+
summary: 'Import OttoRouter wallet from private key',
|
|
106
106
|
requestBody: {
|
|
107
107
|
required: true,
|
|
108
108
|
content: {
|
|
@@ -137,11 +137,11 @@ export const authPaths = {
|
|
|
137
137
|
},
|
|
138
138
|
},
|
|
139
139
|
},
|
|
140
|
-
'/v1/auth/
|
|
140
|
+
'/v1/auth/ottorouter/export': {
|
|
141
141
|
get: {
|
|
142
142
|
tags: ['auth'],
|
|
143
|
-
operationId: '
|
|
144
|
-
summary: 'Export
|
|
143
|
+
operationId: 'exportOttoRouterWallet',
|
|
144
|
+
summary: 'Export OttoRouter wallet private key',
|
|
145
145
|
responses: {
|
|
146
146
|
200: {
|
|
147
147
|
description: 'OK',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export const
|
|
2
|
-
'/v1/
|
|
1
|
+
export const ottorouterPaths = {
|
|
2
|
+
'/v1/ottorouter/balance': {
|
|
3
3
|
get: {
|
|
4
|
-
tags: ['
|
|
5
|
-
operationId: '
|
|
6
|
-
summary: 'Get
|
|
4
|
+
tags: ['ottorouter'],
|
|
5
|
+
operationId: 'getOttoRouterBalance',
|
|
6
|
+
summary: 'Get OttoRouter account balance',
|
|
7
7
|
description:
|
|
8
8
|
'Returns wallet balance, subscription, account info, limits, and usage data',
|
|
9
9
|
responses: {
|
|
@@ -82,7 +82,7 @@ export const setuPaths = {
|
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
84
|
502: {
|
|
85
|
-
description: 'Failed to fetch balance from
|
|
85
|
+
description: 'Failed to fetch balance from OttoRouter',
|
|
86
86
|
content: {
|
|
87
87
|
'application/json': {
|
|
88
88
|
schema: {
|
|
@@ -96,11 +96,11 @@ export const setuPaths = {
|
|
|
96
96
|
},
|
|
97
97
|
},
|
|
98
98
|
},
|
|
99
|
-
'/v1/
|
|
99
|
+
'/v1/ottorouter/wallet': {
|
|
100
100
|
get: {
|
|
101
|
-
tags: ['
|
|
102
|
-
operationId: '
|
|
103
|
-
summary: 'Get
|
|
101
|
+
tags: ['ottorouter'],
|
|
102
|
+
operationId: 'getOttoRouterWallet',
|
|
103
|
+
summary: 'Get OttoRouter wallet info',
|
|
104
104
|
description:
|
|
105
105
|
'Returns whether the wallet is configured and its public key',
|
|
106
106
|
responses: {
|
|
@@ -123,10 +123,10 @@ export const setuPaths = {
|
|
|
123
123
|
},
|
|
124
124
|
},
|
|
125
125
|
},
|
|
126
|
-
'/v1/
|
|
126
|
+
'/v1/ottorouter/usdc-balance': {
|
|
127
127
|
get: {
|
|
128
|
-
tags: ['
|
|
129
|
-
operationId: '
|
|
128
|
+
tags: ['ottorouter'],
|
|
129
|
+
operationId: 'getOttoRouterUsdcBalance',
|
|
130
130
|
summary: 'Get USDC token balance',
|
|
131
131
|
description:
|
|
132
132
|
'Fetches USDC balance from Solana blockchain for the configured wallet',
|
|
@@ -189,9 +189,9 @@ export const setuPaths = {
|
|
|
189
189
|
},
|
|
190
190
|
},
|
|
191
191
|
},
|
|
192
|
-
'/v1/
|
|
192
|
+
'/v1/ottorouter/topup/polar': {
|
|
193
193
|
post: {
|
|
194
|
-
tags: ['
|
|
194
|
+
tags: ['ottorouter'],
|
|
195
195
|
operationId: 'createPolarCheckout',
|
|
196
196
|
summary: 'Create a Polar checkout for topping up',
|
|
197
197
|
requestBody: {
|
|
@@ -233,9 +233,9 @@ export const setuPaths = {
|
|
|
233
233
|
},
|
|
234
234
|
},
|
|
235
235
|
},
|
|
236
|
-
'/v1/
|
|
236
|
+
'/v1/ottorouter/topup/select': {
|
|
237
237
|
post: {
|
|
238
|
-
tags: ['
|
|
238
|
+
tags: ['ottorouter'],
|
|
239
239
|
operationId: 'selectTopupMethod',
|
|
240
240
|
summary: 'Select topup method for pending request',
|
|
241
241
|
requestBody: {
|
|
@@ -287,9 +287,9 @@ export const setuPaths = {
|
|
|
287
287
|
},
|
|
288
288
|
},
|
|
289
289
|
},
|
|
290
|
-
'/v1/
|
|
290
|
+
'/v1/ottorouter/topup/cancel': {
|
|
291
291
|
post: {
|
|
292
|
-
tags: ['
|
|
292
|
+
tags: ['ottorouter'],
|
|
293
293
|
operationId: 'cancelTopup',
|
|
294
294
|
summary: 'Cancel pending topup',
|
|
295
295
|
requestBody: {
|
|
@@ -337,9 +337,9 @@ export const setuPaths = {
|
|
|
337
337
|
},
|
|
338
338
|
},
|
|
339
339
|
},
|
|
340
|
-
'/v1/
|
|
340
|
+
'/v1/ottorouter/topup/pending': {
|
|
341
341
|
get: {
|
|
342
|
-
tags: ['
|
|
342
|
+
tags: ['ottorouter'],
|
|
343
343
|
operationId: 'getPendingTopup',
|
|
344
344
|
summary: 'Get pending topup for a session',
|
|
345
345
|
parameters: [
|
|
@@ -373,9 +373,9 @@ export const setuPaths = {
|
|
|
373
373
|
},
|
|
374
374
|
},
|
|
375
375
|
},
|
|
376
|
-
'/v1/
|
|
376
|
+
'/v1/ottorouter/topup/polar/estimate': {
|
|
377
377
|
get: {
|
|
378
|
-
tags: ['
|
|
378
|
+
tags: ['ottorouter'],
|
|
379
379
|
operationId: 'getPolarTopupEstimate',
|
|
380
380
|
summary: 'Get estimated fees for a Polar topup',
|
|
381
381
|
parameters: [
|
|
@@ -414,9 +414,9 @@ export const setuPaths = {
|
|
|
414
414
|
},
|
|
415
415
|
},
|
|
416
416
|
},
|
|
417
|
-
'/v1/
|
|
417
|
+
'/v1/ottorouter/topup/polar/status': {
|
|
418
418
|
get: {
|
|
419
|
-
tags: ['
|
|
419
|
+
tags: ['ottorouter'],
|
|
420
420
|
operationId: 'getPolarTopupStatus',
|
|
421
421
|
summary: 'Get status of a Polar checkout',
|
|
422
422
|
parameters: [
|
|
@@ -447,9 +447,9 @@ export const setuPaths = {
|
|
|
447
447
|
},
|
|
448
448
|
},
|
|
449
449
|
},
|
|
450
|
-
'/v1/
|
|
450
|
+
'/v1/ottorouter/topup/razorpay/estimate': {
|
|
451
451
|
get: {
|
|
452
|
-
tags: ['
|
|
452
|
+
tags: ['ottorouter'],
|
|
453
453
|
operationId: 'getRazorpayTopupEstimate',
|
|
454
454
|
summary: 'Get estimated fees for a Razorpay topup',
|
|
455
455
|
parameters: [
|
|
@@ -482,9 +482,9 @@ export const setuPaths = {
|
|
|
482
482
|
},
|
|
483
483
|
},
|
|
484
484
|
},
|
|
485
|
-
'/v1/
|
|
485
|
+
'/v1/ottorouter/topup/razorpay': {
|
|
486
486
|
post: {
|
|
487
|
-
tags: ['
|
|
487
|
+
tags: ['ottorouter'],
|
|
488
488
|
operationId: 'createRazorpayOrder',
|
|
489
489
|
summary: 'Create a Razorpay order for topping up',
|
|
490
490
|
requestBody: {
|
|
@@ -535,9 +535,9 @@ export const setuPaths = {
|
|
|
535
535
|
},
|
|
536
536
|
},
|
|
537
537
|
},
|
|
538
|
-
'/v1/
|
|
538
|
+
'/v1/ottorouter/topup/razorpay/verify': {
|
|
539
539
|
post: {
|
|
540
|
-
tags: ['
|
|
540
|
+
tags: ['ottorouter'],
|
|
541
541
|
operationId: 'verifyRazorpayPayment',
|
|
542
542
|
summary: 'Verify Razorpay payment and credit balance',
|
|
543
543
|
requestBody: {
|
package/src/openapi/spec.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { sessionApprovalPaths } from './paths/session-approval';
|
|
|
13
13
|
import { sessionExtrasPaths } from './paths/session-extras';
|
|
14
14
|
import { sessionFilesPaths } from './paths/session-files';
|
|
15
15
|
import { sessionsPaths } from './paths/sessions';
|
|
16
|
-
import {
|
|
16
|
+
import { ottorouterPaths } from './paths/ottorouter';
|
|
17
17
|
import { skillsPaths } from './paths/skills';
|
|
18
18
|
import { streamPaths } from './paths/stream';
|
|
19
19
|
import { terminalsPath } from './paths/terminals';
|
|
@@ -38,7 +38,7 @@ export function getOpenAPISpec() {
|
|
|
38
38
|
{ name: 'files' },
|
|
39
39
|
{ name: 'git' },
|
|
40
40
|
{ name: 'terminals' },
|
|
41
|
-
{ name: '
|
|
41
|
+
{ name: 'ottorouter' },
|
|
42
42
|
{ name: 'auth' },
|
|
43
43
|
{ name: 'mcp' },
|
|
44
44
|
{ name: 'tunnel' },
|
|
@@ -59,7 +59,7 @@ export function getOpenAPISpec() {
|
|
|
59
59
|
...sessionExtrasPaths,
|
|
60
60
|
...sessionFilesPaths,
|
|
61
61
|
...sessionsPaths,
|
|
62
|
-
...
|
|
62
|
+
...ottorouterPaths,
|
|
63
63
|
...skillsPaths,
|
|
64
64
|
...streamPaths,
|
|
65
65
|
...terminalsPath,
|
package/src/routes/auth.ts
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
getAuth,
|
|
5
5
|
setAuth,
|
|
6
6
|
removeAuth,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
ensureOttoRouterWallet,
|
|
8
|
+
getOttoRouterWallet,
|
|
9
9
|
importWallet,
|
|
10
10
|
loadConfig,
|
|
11
11
|
catalog,
|
|
@@ -223,7 +223,7 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
223
223
|
const auth = await getAllAuth(projectRoot);
|
|
224
224
|
const cfg = await loadConfig(projectRoot);
|
|
225
225
|
const onboardingComplete = await getOnboardingComplete(projectRoot);
|
|
226
|
-
const
|
|
226
|
+
const ottorouterWallet = await getOttoRouterWallet(projectRoot);
|
|
227
227
|
const ghImportCapability = getGhImportCapability();
|
|
228
228
|
|
|
229
229
|
const providers: Record<
|
|
@@ -269,10 +269,10 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
269
269
|
|
|
270
270
|
return c.json({
|
|
271
271
|
onboardingComplete,
|
|
272
|
-
|
|
272
|
+
ottorouter: ottorouterWallet
|
|
273
273
|
? {
|
|
274
274
|
configured: true,
|
|
275
|
-
publicKey:
|
|
275
|
+
publicKey: ottorouterWallet.publicKey,
|
|
276
276
|
}
|
|
277
277
|
: {
|
|
278
278
|
configured: false,
|
|
@@ -287,11 +287,11 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
287
287
|
}
|
|
288
288
|
});
|
|
289
289
|
|
|
290
|
-
app.post('/v1/auth/
|
|
290
|
+
app.post('/v1/auth/ottorouter/setup', async (c) => {
|
|
291
291
|
try {
|
|
292
292
|
const projectRoot = process.cwd();
|
|
293
|
-
const existing = await
|
|
294
|
-
const wallet = await
|
|
293
|
+
const existing = await getOttoRouterWallet(projectRoot);
|
|
294
|
+
const wallet = await ensureOttoRouterWallet(projectRoot);
|
|
295
295
|
|
|
296
296
|
return c.json({
|
|
297
297
|
success: true,
|
|
@@ -299,13 +299,13 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
299
299
|
isNew: !existing,
|
|
300
300
|
});
|
|
301
301
|
} catch (error) {
|
|
302
|
-
logger.error('Failed to setup
|
|
302
|
+
logger.error('Failed to setup OttoRouter wallet', error);
|
|
303
303
|
const errorResponse = serializeError(error);
|
|
304
304
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
305
305
|
}
|
|
306
306
|
});
|
|
307
307
|
|
|
308
|
-
app.post('/v1/auth/
|
|
308
|
+
app.post('/v1/auth/ottorouter/import', async (c) => {
|
|
309
309
|
try {
|
|
310
310
|
const { privateKey } = await c.req.json<{ privateKey: string }>();
|
|
311
311
|
|
|
@@ -316,7 +316,7 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
316
316
|
try {
|
|
317
317
|
const wallet = importWallet(privateKey);
|
|
318
318
|
await setAuth(
|
|
319
|
-
'
|
|
319
|
+
'ottorouter',
|
|
320
320
|
{ type: 'wallet', secret: privateKey },
|
|
321
321
|
undefined,
|
|
322
322
|
'global',
|
|
@@ -330,19 +330,19 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
330
330
|
return c.json({ error: 'Invalid private key format' }, 400);
|
|
331
331
|
}
|
|
332
332
|
} catch (error) {
|
|
333
|
-
logger.error('Failed to import
|
|
333
|
+
logger.error('Failed to import OttoRouter wallet', error);
|
|
334
334
|
const errorResponse = serializeError(error);
|
|
335
335
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
336
336
|
}
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
-
app.get('/v1/auth/
|
|
339
|
+
app.get('/v1/auth/ottorouter/export', async (c) => {
|
|
340
340
|
try {
|
|
341
341
|
const projectRoot = process.cwd();
|
|
342
|
-
const wallet = await
|
|
342
|
+
const wallet = await getOttoRouterWallet(projectRoot);
|
|
343
343
|
|
|
344
344
|
if (!wallet) {
|
|
345
|
-
return c.json({ error: '
|
|
345
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 404);
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
return c.json({
|
|
@@ -351,7 +351,7 @@ export function registerAuthRoutes(app: Hono) {
|
|
|
351
351
|
privateKey: wallet.privateKey,
|
|
352
352
|
});
|
|
353
353
|
} catch (error) {
|
|
354
|
-
logger.error('Failed to export
|
|
354
|
+
logger.error('Failed to export OttoRouter wallet', error);
|
|
355
355
|
const errorResponse = serializeError(error);
|
|
356
356
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
357
357
|
}
|
package/src/routes/doctor.ts
CHANGED
|
@@ -20,7 +20,7 @@ const PROVIDERS: ProviderId[] = [
|
|
|
20
20
|
'google',
|
|
21
21
|
'openrouter',
|
|
22
22
|
'opencode',
|
|
23
|
-
'
|
|
23
|
+
'ottorouter',
|
|
24
24
|
];
|
|
25
25
|
|
|
26
26
|
function providerEnvVar(p: ProviderId): string | null {
|
|
@@ -28,7 +28,7 @@ function providerEnvVar(p: ProviderId): string | null {
|
|
|
28
28
|
if (p === 'anthropic') return 'ANTHROPIC_API_KEY';
|
|
29
29
|
if (p === 'google') return 'GOOGLE_GENERATIVE_AI_API_KEY';
|
|
30
30
|
if (p === 'opencode') return 'OPENCODE_API_KEY';
|
|
31
|
-
if (p === '
|
|
31
|
+
if (p === 'ottorouter') return 'OTTOROUTER_PRIVATE_KEY';
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Hono } from 'hono';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
fetchOttoRouterBalance,
|
|
4
4
|
getPublicKeyFromPrivate,
|
|
5
5
|
getAuth,
|
|
6
6
|
loadConfig,
|
|
@@ -18,23 +18,23 @@ import {
|
|
|
18
18
|
type TopupMethod,
|
|
19
19
|
} from '../runtime/topup/manager.ts';
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
process.env.
|
|
21
|
+
const OTTOROUTER_BASE_URL =
|
|
22
|
+
process.env.OTTOROUTER_BASE_URL || 'https://api.ottorouter.org';
|
|
23
23
|
|
|
24
|
-
function
|
|
25
|
-
return
|
|
26
|
-
?
|
|
27
|
-
:
|
|
24
|
+
function getOttoRouterBaseUrl(): string {
|
|
25
|
+
return OTTOROUTER_BASE_URL.endsWith('/')
|
|
26
|
+
? OTTOROUTER_BASE_URL.slice(0, -1)
|
|
27
|
+
: OTTOROUTER_BASE_URL;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async function
|
|
31
|
-
if (process.env.
|
|
32
|
-
return process.env.
|
|
30
|
+
async function getOttoRouterPrivateKey(): Promise<string | null> {
|
|
31
|
+
if (process.env.OTTOROUTER_PRIVATE_KEY) {
|
|
32
|
+
return process.env.OTTOROUTER_PRIVATE_KEY;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
try {
|
|
36
36
|
const cfg = await loadConfig(process.cwd());
|
|
37
|
-
const auth = await getAuth('
|
|
37
|
+
const auth = await getAuth('ottorouter', cfg.projectRoot);
|
|
38
38
|
if (auth?.type === 'wallet' && auth.secret) {
|
|
39
39
|
return auth.secret;
|
|
40
40
|
}
|
|
@@ -62,33 +62,36 @@ function buildWalletHeaders(privateKey: string): Record<string, string> {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export function
|
|
66
|
-
app.get('/v1/
|
|
65
|
+
export function registerOttoRouterRoutes(app: Hono) {
|
|
66
|
+
app.get('/v1/ottorouter/balance', async (c) => {
|
|
67
67
|
try {
|
|
68
|
-
const privateKey = await
|
|
68
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
69
69
|
if (!privateKey) {
|
|
70
|
-
return c.json({ error: '
|
|
70
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 401);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const balance = await
|
|
73
|
+
const balance = await fetchOttoRouterBalance({ privateKey });
|
|
74
74
|
if (!balance) {
|
|
75
|
-
return c.json(
|
|
75
|
+
return c.json(
|
|
76
|
+
{ error: 'Failed to fetch balance from OttoRouter' },
|
|
77
|
+
502,
|
|
78
|
+
);
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
return c.json(balance);
|
|
79
82
|
} catch (error) {
|
|
80
|
-
logger.error('Failed to fetch
|
|
83
|
+
logger.error('Failed to fetch OttoRouter balance', error);
|
|
81
84
|
const errorResponse = serializeError(error);
|
|
82
85
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
83
86
|
}
|
|
84
87
|
});
|
|
85
88
|
|
|
86
|
-
app.get('/v1/
|
|
89
|
+
app.get('/v1/ottorouter/wallet', async (c) => {
|
|
87
90
|
try {
|
|
88
|
-
const privateKey = await
|
|
91
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
89
92
|
if (!privateKey) {
|
|
90
93
|
return c.json(
|
|
91
|
-
{ error: '
|
|
94
|
+
{ error: 'OttoRouter wallet not configured', configured: false },
|
|
92
95
|
200,
|
|
93
96
|
);
|
|
94
97
|
}
|
|
@@ -103,17 +106,17 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
103
106
|
publicKey,
|
|
104
107
|
});
|
|
105
108
|
} catch (error) {
|
|
106
|
-
logger.error('Failed to get
|
|
109
|
+
logger.error('Failed to get OttoRouter wallet info', error);
|
|
107
110
|
const errorResponse = serializeError(error);
|
|
108
111
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
109
112
|
}
|
|
110
113
|
});
|
|
111
114
|
|
|
112
|
-
app.get('/v1/
|
|
115
|
+
app.get('/v1/ottorouter/usdc-balance', async (c) => {
|
|
113
116
|
try {
|
|
114
|
-
const privateKey = await
|
|
117
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
115
118
|
if (!privateKey) {
|
|
116
|
-
return c.json({ error: '
|
|
119
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 401);
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
const publicKey = getPublicKeyFromPrivate(privateKey);
|
|
@@ -121,7 +124,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
121
124
|
return c.json({ error: 'Invalid private key' }, 400);
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
const baseUrl =
|
|
127
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
125
128
|
const response = await fetch(
|
|
126
129
|
`${baseUrl}/v1/wallet/${publicKey}/balances?limit=100&showNative=false&showNfts=false&showZeroBalance=false`,
|
|
127
130
|
{
|
|
@@ -161,14 +164,14 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
161
164
|
}
|
|
162
165
|
});
|
|
163
166
|
|
|
164
|
-
app.get('/v1/
|
|
167
|
+
app.get('/v1/ottorouter/topup/polar/estimate', async (c) => {
|
|
165
168
|
try {
|
|
166
169
|
const amount = c.req.query('amount');
|
|
167
170
|
if (!amount) {
|
|
168
171
|
return c.json({ error: 'Missing amount parameter' }, 400);
|
|
169
172
|
}
|
|
170
173
|
|
|
171
|
-
const baseUrl =
|
|
174
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
172
175
|
const response = await fetch(
|
|
173
176
|
`${baseUrl}/v1/topup/polar/estimate?amount=${amount}`,
|
|
174
177
|
{
|
|
@@ -190,11 +193,11 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
190
193
|
}
|
|
191
194
|
});
|
|
192
195
|
|
|
193
|
-
app.post('/v1/
|
|
196
|
+
app.post('/v1/ottorouter/topup/polar', async (c) => {
|
|
194
197
|
try {
|
|
195
|
-
const privateKey = await
|
|
198
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
196
199
|
if (!privateKey) {
|
|
197
|
-
return c.json({ error: '
|
|
200
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 401);
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
const body = await c.req.json();
|
|
@@ -212,7 +215,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
const walletHeaders = buildWalletHeaders(privateKey);
|
|
215
|
-
const baseUrl =
|
|
218
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
216
219
|
|
|
217
220
|
const response = await fetch(`${baseUrl}/v1/topup/polar`, {
|
|
218
221
|
method: 'POST',
|
|
@@ -236,7 +239,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
236
239
|
}
|
|
237
240
|
});
|
|
238
241
|
|
|
239
|
-
app.post('/v1/
|
|
242
|
+
app.post('/v1/ottorouter/topup/select', async (c) => {
|
|
240
243
|
try {
|
|
241
244
|
const body = await c.req.json();
|
|
242
245
|
const { sessionId, method } = body as {
|
|
@@ -264,7 +267,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
264
267
|
}
|
|
265
268
|
|
|
266
269
|
publish({
|
|
267
|
-
type: '
|
|
270
|
+
type: 'ottorouter.topup.method_selected',
|
|
268
271
|
sessionId,
|
|
269
272
|
payload: { method },
|
|
270
273
|
});
|
|
@@ -277,7 +280,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
277
280
|
}
|
|
278
281
|
});
|
|
279
282
|
|
|
280
|
-
app.post('/v1/
|
|
283
|
+
app.post('/v1/ottorouter/topup/cancel', async (c) => {
|
|
281
284
|
try {
|
|
282
285
|
const body = await c.req.json();
|
|
283
286
|
const { sessionId, reason } = body as {
|
|
@@ -301,7 +304,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
301
304
|
}
|
|
302
305
|
|
|
303
306
|
publish({
|
|
304
|
-
type: '
|
|
307
|
+
type: 'ottorouter.topup.cancelled',
|
|
305
308
|
sessionId,
|
|
306
309
|
payload: { reason: reason ?? 'User cancelled' },
|
|
307
310
|
});
|
|
@@ -314,7 +317,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
314
317
|
}
|
|
315
318
|
});
|
|
316
319
|
|
|
317
|
-
app.get('/v1/
|
|
320
|
+
app.get('/v1/ottorouter/topup/pending', async (c) => {
|
|
318
321
|
try {
|
|
319
322
|
const sessionId = c.req.query('sessionId');
|
|
320
323
|
if (!sessionId) {
|
|
@@ -341,14 +344,14 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
341
344
|
}
|
|
342
345
|
});
|
|
343
346
|
|
|
344
|
-
app.get('/v1/
|
|
347
|
+
app.get('/v1/ottorouter/topup/polar/status', async (c) => {
|
|
345
348
|
try {
|
|
346
349
|
const checkoutId = c.req.query('checkoutId');
|
|
347
350
|
if (!checkoutId) {
|
|
348
351
|
return c.json({ error: 'Missing checkoutId parameter' }, 400);
|
|
349
352
|
}
|
|
350
353
|
|
|
351
|
-
const baseUrl =
|
|
354
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
352
355
|
const response = await fetch(
|
|
353
356
|
`${baseUrl}/v1/topup/polar/status?checkoutId=${checkoutId}`,
|
|
354
357
|
{
|
|
@@ -370,14 +373,14 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
370
373
|
}
|
|
371
374
|
});
|
|
372
375
|
|
|
373
|
-
app.get('/v1/
|
|
376
|
+
app.get('/v1/ottorouter/topup/razorpay/estimate', async (c) => {
|
|
374
377
|
try {
|
|
375
378
|
const amount = c.req.query('amount');
|
|
376
379
|
if (!amount) {
|
|
377
380
|
return c.json({ error: 'Missing amount parameter' }, 400);
|
|
378
381
|
}
|
|
379
382
|
|
|
380
|
-
const baseUrl =
|
|
383
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
381
384
|
const response = await fetch(
|
|
382
385
|
`${baseUrl}/v1/topup/razorpay/estimate?amount=${amount}`,
|
|
383
386
|
{
|
|
@@ -399,11 +402,11 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
399
402
|
}
|
|
400
403
|
});
|
|
401
404
|
|
|
402
|
-
app.post('/v1/
|
|
405
|
+
app.post('/v1/ottorouter/topup/razorpay', async (c) => {
|
|
403
406
|
try {
|
|
404
|
-
const privateKey = await
|
|
407
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
405
408
|
if (!privateKey) {
|
|
406
|
-
return c.json({ error: '
|
|
409
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 401);
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
const body = await c.req.json();
|
|
@@ -414,7 +417,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
414
417
|
}
|
|
415
418
|
|
|
416
419
|
const walletHeaders = buildWalletHeaders(privateKey);
|
|
417
|
-
const baseUrl =
|
|
420
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
418
421
|
|
|
419
422
|
const response = await fetch(`${baseUrl}/v1/topup/razorpay`, {
|
|
420
423
|
method: 'POST',
|
|
@@ -438,11 +441,11 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
438
441
|
}
|
|
439
442
|
});
|
|
440
443
|
|
|
441
|
-
app.post('/v1/
|
|
444
|
+
app.post('/v1/ottorouter/topup/razorpay/verify', async (c) => {
|
|
442
445
|
try {
|
|
443
|
-
const privateKey = await
|
|
446
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
444
447
|
if (!privateKey) {
|
|
445
|
-
return c.json({ error: '
|
|
448
|
+
return c.json({ error: 'OttoRouter wallet not configured' }, 401);
|
|
446
449
|
}
|
|
447
450
|
|
|
448
451
|
const body = await c.req.json();
|
|
@@ -458,7 +461,7 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
458
461
|
}
|
|
459
462
|
|
|
460
463
|
const walletHeaders = buildWalletHeaders(privateKey);
|
|
461
|
-
const baseUrl =
|
|
464
|
+
const baseUrl = getOttoRouterBaseUrl();
|
|
462
465
|
|
|
463
466
|
const response = await fetch(`${baseUrl}/v1/topup/razorpay/verify`, {
|
|
464
467
|
method: 'POST',
|
|
@@ -137,7 +137,7 @@ async function processAskRequest(
|
|
|
137
137
|
openrouter: { enabled: true },
|
|
138
138
|
opencode: { enabled: true },
|
|
139
139
|
copilot: { enabled: true },
|
|
140
|
-
|
|
140
|
+
ottorouter: { enabled: true },
|
|
141
141
|
zai: { enabled: true },
|
|
142
142
|
'zai-coding': { enabled: true },
|
|
143
143
|
moonshot: { enabled: true },
|
|
@@ -3,7 +3,10 @@ import { getAnthropicInstance } from './anthropic.ts';
|
|
|
3
3
|
import { resolveOpenAIModel } from './openai.ts';
|
|
4
4
|
import { resolveGoogleModel } from './google.ts';
|
|
5
5
|
import { resolveOpenRouterModel } from './openrouter.ts';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
resolveOttoRouterModel,
|
|
8
|
+
type ResolveOttoRouterModelOptions,
|
|
9
|
+
} from './ottorouter.ts';
|
|
7
10
|
import { getZaiInstance, getZaiCodingInstance } from './zai.ts';
|
|
8
11
|
import { resolveOpencodeModel } from './opencode.ts';
|
|
9
12
|
import { getMoonshotInstance } from './moonshot.ts';
|
|
@@ -20,8 +23,8 @@ export async function resolveModel(
|
|
|
20
23
|
systemPrompt?: string;
|
|
21
24
|
sessionId?: string;
|
|
22
25
|
messageId?: string;
|
|
23
|
-
topupApprovalMode?:
|
|
24
|
-
autoPayThresholdUsd?:
|
|
26
|
+
topupApprovalMode?: ResolveOttoRouterModelOptions['topupApprovalMode'];
|
|
27
|
+
autoPayThresholdUsd?: ResolveOttoRouterModelOptions['autoPayThresholdUsd'];
|
|
25
28
|
},
|
|
26
29
|
) {
|
|
27
30
|
if (provider === 'openai') {
|
|
@@ -43,8 +46,8 @@ export async function resolveModel(
|
|
|
43
46
|
if (provider === 'copilot') {
|
|
44
47
|
return resolveCopilotModel(model, cfg);
|
|
45
48
|
}
|
|
46
|
-
if (provider === '
|
|
47
|
-
return await
|
|
49
|
+
if (provider === 'ottorouter') {
|
|
50
|
+
return await resolveOttoRouterModel(model, options?.sessionId, {
|
|
48
51
|
messageId: options?.messageId,
|
|
49
52
|
topupApprovalMode: options?.topupApprovalMode,
|
|
50
53
|
autoPayThresholdUsd: options?.autoPayThresholdUsd,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
type
|
|
2
|
+
createOttoRouter,
|
|
3
|
+
type OttoRouterPaymentCallbacks,
|
|
4
4
|
getAuth,
|
|
5
5
|
loadConfig,
|
|
6
6
|
} from '@ottocode/sdk';
|
|
@@ -14,19 +14,19 @@ import {
|
|
|
14
14
|
|
|
15
15
|
const MIN_TOPUP_USD = 5;
|
|
16
16
|
|
|
17
|
-
export interface
|
|
17
|
+
export interface ResolveOttoRouterModelOptions {
|
|
18
18
|
messageId?: string;
|
|
19
19
|
topupApprovalMode?: 'auto' | 'approval';
|
|
20
20
|
autoPayThresholdUsd?: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async function
|
|
24
|
-
if (process.env.
|
|
25
|
-
return process.env.
|
|
23
|
+
async function getOttoRouterPrivateKey(): Promise<string> {
|
|
24
|
+
if (process.env.OTTOROUTER_PRIVATE_KEY) {
|
|
25
|
+
return process.env.OTTOROUTER_PRIVATE_KEY;
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
28
|
const cfg = await loadConfig(process.cwd());
|
|
29
|
-
const auth = await getAuth('
|
|
29
|
+
const auth = await getAuth('ottorouter', cfg.projectRoot);
|
|
30
30
|
if (auth?.type === 'wallet' && auth.secret) {
|
|
31
31
|
return auth.secret;
|
|
32
32
|
}
|
|
@@ -34,58 +34,58 @@ async function getSetuPrivateKey(): Promise<string> {
|
|
|
34
34
|
return '';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export async function
|
|
37
|
+
export async function resolveOttoRouterModel(
|
|
38
38
|
model: string,
|
|
39
39
|
sessionId?: string,
|
|
40
|
-
options:
|
|
40
|
+
options: ResolveOttoRouterModelOptions = {},
|
|
41
41
|
) {
|
|
42
|
-
const privateKey = await
|
|
42
|
+
const privateKey = await getOttoRouterPrivateKey();
|
|
43
43
|
if (!privateKey) {
|
|
44
44
|
throw new Error(
|
|
45
|
-
'
|
|
45
|
+
'OttoRouter provider requires OTTOROUTER_PRIVATE_KEY (base58 Solana secret).',
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
|
-
const baseURL = process.env.
|
|
49
|
-
const rpcURL = process.env.
|
|
48
|
+
const baseURL = process.env.OTTOROUTER_BASE_URL;
|
|
49
|
+
const rpcURL = process.env.OTTOROUTER_SOLANA_RPC_URL;
|
|
50
50
|
const {
|
|
51
51
|
messageId,
|
|
52
52
|
topupApprovalMode = 'approval',
|
|
53
53
|
autoPayThresholdUsd = MIN_TOPUP_USD,
|
|
54
54
|
} = options;
|
|
55
55
|
|
|
56
|
-
const callbacks:
|
|
56
|
+
const callbacks: OttoRouterPaymentCallbacks = sessionId
|
|
57
57
|
? {
|
|
58
58
|
onPaymentRequired: (amountUsd, currentBalance) => {
|
|
59
59
|
publish({
|
|
60
|
-
type: '
|
|
60
|
+
type: 'ottorouter.payment.required',
|
|
61
61
|
sessionId,
|
|
62
62
|
payload: { amountUsd, currentBalance },
|
|
63
63
|
});
|
|
64
64
|
},
|
|
65
65
|
onPaymentSigning: () => {
|
|
66
66
|
publish({
|
|
67
|
-
type: '
|
|
67
|
+
type: 'ottorouter.payment.signing',
|
|
68
68
|
sessionId,
|
|
69
69
|
payload: {},
|
|
70
70
|
});
|
|
71
71
|
},
|
|
72
72
|
onPaymentComplete: (data) => {
|
|
73
73
|
publish({
|
|
74
|
-
type: '
|
|
74
|
+
type: 'ottorouter.payment.complete',
|
|
75
75
|
sessionId,
|
|
76
76
|
payload: data,
|
|
77
77
|
});
|
|
78
78
|
},
|
|
79
79
|
onPaymentError: (error) => {
|
|
80
80
|
publish({
|
|
81
|
-
type: '
|
|
81
|
+
type: 'ottorouter.payment.error',
|
|
82
82
|
sessionId,
|
|
83
83
|
payload: { error },
|
|
84
84
|
});
|
|
85
85
|
},
|
|
86
86
|
onBalanceUpdate: (update) => {
|
|
87
87
|
publish({
|
|
88
|
-
type: '
|
|
88
|
+
type: 'ottorouter.balance.updated',
|
|
89
89
|
sessionId,
|
|
90
90
|
payload: update,
|
|
91
91
|
});
|
|
@@ -97,7 +97,7 @@ export async function resolveSetuModel(
|
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
publish({
|
|
100
|
-
type: '
|
|
100
|
+
type: 'ottorouter.topup.required',
|
|
101
101
|
sessionId,
|
|
102
102
|
payload: {
|
|
103
103
|
messageId,
|
|
@@ -118,7 +118,7 @@ export async function resolveSetuModel(
|
|
|
118
118
|
}
|
|
119
119
|
: {};
|
|
120
120
|
|
|
121
|
-
const
|
|
121
|
+
const ottorouter = createOttoRouter({
|
|
122
122
|
auth: { privateKey },
|
|
123
123
|
baseURL,
|
|
124
124
|
rpcURL,
|
|
@@ -130,5 +130,5 @@ export async function resolveSetuModel(
|
|
|
130
130
|
},
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
return
|
|
133
|
+
return ottorouter.model(model);
|
|
134
134
|
}
|
|
@@ -28,7 +28,7 @@ export function createErrorHandler(
|
|
|
28
28
|
| undefined;
|
|
29
29
|
const causeError = errObj?.cause as Record<string, unknown> | undefined;
|
|
30
30
|
|
|
31
|
-
// Check for
|
|
31
|
+
// Check for OTTOROUTER_FIAT_SELECTED code specifically (not string matching)
|
|
32
32
|
const errorCode =
|
|
33
33
|
(errObj?.code as string) ??
|
|
34
34
|
((errObj?.error as Record<string, unknown>)?.code as string) ??
|
|
@@ -72,7 +72,7 @@ export function createErrorHandler(
|
|
|
72
72
|
(causeError?.message as string) ??
|
|
73
73
|
'';
|
|
74
74
|
|
|
75
|
-
const isFiatSelected = errorCode === '
|
|
75
|
+
const isFiatSelected = errorCode === 'OTTOROUTER_FIAT_SELECTED';
|
|
76
76
|
|
|
77
77
|
// Handle fiat payment selected - this is not an error, just a signal to pause
|
|
78
78
|
if (isFiatSelected) {
|
|
@@ -140,7 +140,7 @@ export function createErrorHandler(
|
|
|
140
140
|
|
|
141
141
|
// Emit a special event so UI knows to show topup modal
|
|
142
142
|
publish({
|
|
143
|
-
type: '
|
|
143
|
+
type: 'ottorouter.fiat.checkout_created',
|
|
144
144
|
sessionId: opts.sessionId,
|
|
145
145
|
payload: {
|
|
146
146
|
messageId: opts.assistantMessageId,
|