@ottocode/server 0.1.223 → 0.1.225
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.225",
|
|
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.225",
|
|
53
|
+
"@ottocode/database": "0.1.225",
|
|
54
54
|
"drizzle-orm": "^0.44.5",
|
|
55
55
|
"hono": "^4.9.9",
|
|
56
56
|
"zod": "^4.3.6"
|
|
@@ -447,4 +447,148 @@ export const setuPaths = {
|
|
|
447
447
|
},
|
|
448
448
|
},
|
|
449
449
|
},
|
|
450
|
+
'/v1/setu/topup/razorpay/estimate': {
|
|
451
|
+
get: {
|
|
452
|
+
tags: ['setu'],
|
|
453
|
+
operationId: 'getRazorpayTopupEstimate',
|
|
454
|
+
summary: 'Get estimated fees for a Razorpay topup',
|
|
455
|
+
parameters: [
|
|
456
|
+
{
|
|
457
|
+
in: 'query',
|
|
458
|
+
name: 'amount',
|
|
459
|
+
required: true,
|
|
460
|
+
schema: { type: 'number' },
|
|
461
|
+
description: 'Amount in USD',
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
responses: {
|
|
465
|
+
200: {
|
|
466
|
+
description: 'OK',
|
|
467
|
+
content: {
|
|
468
|
+
'application/json': {
|
|
469
|
+
schema: {
|
|
470
|
+
type: 'object',
|
|
471
|
+
properties: {
|
|
472
|
+
creditAmountUsd: { type: 'number' },
|
|
473
|
+
chargeAmountInr: { type: 'number' },
|
|
474
|
+
feeAmountInr: { type: 'number' },
|
|
475
|
+
currency: { type: 'string' },
|
|
476
|
+
exchangeRate: { type: 'number' },
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
'/v1/setu/topup/razorpay': {
|
|
486
|
+
post: {
|
|
487
|
+
tags: ['setu'],
|
|
488
|
+
operationId: 'createRazorpayOrder',
|
|
489
|
+
summary: 'Create a Razorpay order for topping up',
|
|
490
|
+
requestBody: {
|
|
491
|
+
required: true,
|
|
492
|
+
content: {
|
|
493
|
+
'application/json': {
|
|
494
|
+
schema: {
|
|
495
|
+
type: 'object',
|
|
496
|
+
properties: {
|
|
497
|
+
amount: { type: 'number' },
|
|
498
|
+
},
|
|
499
|
+
required: ['amount'],
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
responses: {
|
|
505
|
+
200: {
|
|
506
|
+
description: 'OK',
|
|
507
|
+
content: {
|
|
508
|
+
'application/json': {
|
|
509
|
+
schema: {
|
|
510
|
+
type: 'object',
|
|
511
|
+
properties: {
|
|
512
|
+
success: { type: 'boolean' },
|
|
513
|
+
orderId: { type: 'string' },
|
|
514
|
+
amount: { type: 'number' },
|
|
515
|
+
currency: { type: 'string' },
|
|
516
|
+
creditAmountUsd: { type: 'number' },
|
|
517
|
+
keyId: { type: 'string' },
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
401: {
|
|
524
|
+
description: 'Wallet not configured',
|
|
525
|
+
content: {
|
|
526
|
+
'application/json': {
|
|
527
|
+
schema: {
|
|
528
|
+
type: 'object',
|
|
529
|
+
properties: { error: { type: 'string' } },
|
|
530
|
+
required: ['error'],
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
'/v1/setu/topup/razorpay/verify': {
|
|
539
|
+
post: {
|
|
540
|
+
tags: ['setu'],
|
|
541
|
+
operationId: 'verifyRazorpayPayment',
|
|
542
|
+
summary: 'Verify Razorpay payment and credit balance',
|
|
543
|
+
requestBody: {
|
|
544
|
+
required: true,
|
|
545
|
+
content: {
|
|
546
|
+
'application/json': {
|
|
547
|
+
schema: {
|
|
548
|
+
type: 'object',
|
|
549
|
+
properties: {
|
|
550
|
+
razorpay_order_id: { type: 'string' },
|
|
551
|
+
razorpay_payment_id: { type: 'string' },
|
|
552
|
+
razorpay_signature: { type: 'string' },
|
|
553
|
+
},
|
|
554
|
+
required: [
|
|
555
|
+
'razorpay_order_id',
|
|
556
|
+
'razorpay_payment_id',
|
|
557
|
+
'razorpay_signature',
|
|
558
|
+
],
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
responses: {
|
|
564
|
+
200: {
|
|
565
|
+
description: 'OK',
|
|
566
|
+
content: {
|
|
567
|
+
'application/json': {
|
|
568
|
+
schema: {
|
|
569
|
+
type: 'object',
|
|
570
|
+
properties: {
|
|
571
|
+
success: { type: 'boolean' },
|
|
572
|
+
credited: { type: 'number' },
|
|
573
|
+
newBalance: { type: 'number' },
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
401: {
|
|
580
|
+
description: 'Wallet not configured',
|
|
581
|
+
content: {
|
|
582
|
+
'application/json': {
|
|
583
|
+
schema: {
|
|
584
|
+
type: 'object',
|
|
585
|
+
properties: { error: { type: 'string' } },
|
|
586
|
+
required: ['error'],
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
450
594
|
} as const;
|
package/src/routes/setu.ts
CHANGED
|
@@ -369,4 +369,120 @@ export function registerSetuRoutes(app: Hono) {
|
|
|
369
369
|
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
370
370
|
}
|
|
371
371
|
});
|
|
372
|
+
|
|
373
|
+
app.get('/v1/setu/topup/razorpay/estimate', async (c) => {
|
|
374
|
+
try {
|
|
375
|
+
const amount = c.req.query('amount');
|
|
376
|
+
if (!amount) {
|
|
377
|
+
return c.json({ error: 'Missing amount parameter' }, 400);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const baseUrl = getSetuBaseUrl();
|
|
381
|
+
const response = await fetch(
|
|
382
|
+
`${baseUrl}/v1/topup/razorpay/estimate?amount=${amount}`,
|
|
383
|
+
{
|
|
384
|
+
method: 'GET',
|
|
385
|
+
headers: { 'Content-Type': 'application/json' },
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const data = await response.json();
|
|
390
|
+
if (!response.ok) {
|
|
391
|
+
return c.json(data, response.status as 400 | 500);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return c.json(data);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
logger.error('Failed to get Razorpay estimate', error);
|
|
397
|
+
const errorResponse = serializeError(error);
|
|
398
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
app.post('/v1/setu/topup/razorpay', async (c) => {
|
|
403
|
+
try {
|
|
404
|
+
const privateKey = await getSetuPrivateKey();
|
|
405
|
+
if (!privateKey) {
|
|
406
|
+
return c.json({ error: 'Setu wallet not configured' }, 401);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const body = await c.req.json();
|
|
410
|
+
const { amount } = body as { amount: number };
|
|
411
|
+
|
|
412
|
+
if (!amount || typeof amount !== 'number') {
|
|
413
|
+
return c.json({ error: 'Invalid amount' }, 400);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const walletHeaders = buildWalletHeaders(privateKey);
|
|
417
|
+
const baseUrl = getSetuBaseUrl();
|
|
418
|
+
|
|
419
|
+
const response = await fetch(`${baseUrl}/v1/topup/razorpay`, {
|
|
420
|
+
method: 'POST',
|
|
421
|
+
headers: {
|
|
422
|
+
'Content-Type': 'application/json',
|
|
423
|
+
...walletHeaders,
|
|
424
|
+
},
|
|
425
|
+
body: JSON.stringify({ amount }),
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const data = await response.json();
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
return c.json(data, response.status as 400 | 500);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return c.json(data);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
logger.error('Failed to create Razorpay order', error);
|
|
436
|
+
const errorResponse = serializeError(error);
|
|
437
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
app.post('/v1/setu/topup/razorpay/verify', async (c) => {
|
|
442
|
+
try {
|
|
443
|
+
const privateKey = await getSetuPrivateKey();
|
|
444
|
+
if (!privateKey) {
|
|
445
|
+
return c.json({ error: 'Setu wallet not configured' }, 401);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const body = await c.req.json();
|
|
449
|
+
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } =
|
|
450
|
+
body as {
|
|
451
|
+
razorpay_order_id: string;
|
|
452
|
+
razorpay_payment_id: string;
|
|
453
|
+
razorpay_signature: string;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
if (!razorpay_order_id || !razorpay_payment_id || !razorpay_signature) {
|
|
457
|
+
return c.json({ error: 'Missing payment details' }, 400);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const walletHeaders = buildWalletHeaders(privateKey);
|
|
461
|
+
const baseUrl = getSetuBaseUrl();
|
|
462
|
+
|
|
463
|
+
const response = await fetch(`${baseUrl}/v1/topup/razorpay/verify`, {
|
|
464
|
+
method: 'POST',
|
|
465
|
+
headers: {
|
|
466
|
+
'Content-Type': 'application/json',
|
|
467
|
+
...walletHeaders,
|
|
468
|
+
},
|
|
469
|
+
body: JSON.stringify({
|
|
470
|
+
razorpay_order_id,
|
|
471
|
+
razorpay_payment_id,
|
|
472
|
+
razorpay_signature,
|
|
473
|
+
}),
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const data = await response.json();
|
|
477
|
+
if (!response.ok) {
|
|
478
|
+
return c.json(data, response.status as 400 | 500);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return c.json(data);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
logger.error('Failed to verify Razorpay payment', error);
|
|
484
|
+
const errorResponse = serializeError(error);
|
|
485
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
372
488
|
}
|
|
@@ -7,6 +7,7 @@ export type OauthCodexContinuationInput = {
|
|
|
7
7
|
finishReason?: string;
|
|
8
8
|
rawFinishReason?: string;
|
|
9
9
|
firstToolSeen: boolean;
|
|
10
|
+
hasTrailingAssistantText: boolean;
|
|
10
11
|
droppedPseudoToolText: boolean;
|
|
11
12
|
lastAssistantText: string;
|
|
12
13
|
};
|
|
@@ -40,6 +41,13 @@ function isTruncatedResponse(
|
|
|
40
41
|
return rawFinishReason === 'max_output_tokens';
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
function isMissingAssistantSummary(
|
|
45
|
+
input: OauthCodexContinuationInput,
|
|
46
|
+
): boolean {
|
|
47
|
+
if (!input.firstToolSeen) return false;
|
|
48
|
+
return !input.hasTrailingAssistantText;
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
const MAX_UNCLEAN_EOF_RETRIES = 1;
|
|
44
52
|
|
|
45
53
|
function isUncleanEof(input: OauthCodexContinuationInput): boolean {
|
|
@@ -68,6 +76,10 @@ export function decideOauthCodexContinuation(
|
|
|
68
76
|
return { shouldContinue: true, reason: 'truncated' };
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
if (isMissingAssistantSummary(input)) {
|
|
80
|
+
return { shouldContinue: true, reason: 'no-trailing-assistant-text' };
|
|
81
|
+
}
|
|
82
|
+
|
|
71
83
|
if (
|
|
72
84
|
isUncleanEof(input) &&
|
|
73
85
|
input.continuationCount < MAX_UNCLEAN_EOF_RETRIES
|
|
@@ -180,7 +180,13 @@ async function runAssistant(opts: RunOpts) {
|
|
|
180
180
|
);
|
|
181
181
|
|
|
182
182
|
let _finishObserved = false;
|
|
183
|
+
let _toolActivityObserved = false;
|
|
184
|
+
let _trailingAssistantTextAfterTool = false;
|
|
183
185
|
const unsubscribeFinish = subscribe(opts.sessionId, (evt) => {
|
|
186
|
+
if (evt.type === 'tool.call' || evt.type === 'tool.result') {
|
|
187
|
+
_toolActivityObserved = true;
|
|
188
|
+
_trailingAssistantTextAfterTool = false;
|
|
189
|
+
}
|
|
184
190
|
if (evt.type !== 'tool.result') return;
|
|
185
191
|
try {
|
|
186
192
|
const name = (evt.payload as { name?: string } | undefined)?.name;
|
|
@@ -287,6 +293,12 @@ async function runAssistant(opts: RunOpts) {
|
|
|
287
293
|
if (accumulated.trim()) {
|
|
288
294
|
latestAssistantText = accumulated;
|
|
289
295
|
}
|
|
296
|
+
if (
|
|
297
|
+
(delta.trim().length > 0 && _toolActivityObserved) ||
|
|
298
|
+
(delta.trim().length > 0 && firstToolSeen())
|
|
299
|
+
) {
|
|
300
|
+
_trailingAssistantTextAfterTool = true;
|
|
301
|
+
}
|
|
290
302
|
|
|
291
303
|
if (!currentPartId && !accumulated.trim()) {
|
|
292
304
|
continue;
|
|
@@ -404,7 +416,7 @@ async function runAssistant(opts: RunOpts) {
|
|
|
404
416
|
}
|
|
405
417
|
|
|
406
418
|
debugLog(
|
|
407
|
-
`[RUNNER] Stream finished. finishSeen=${_finishObserved}, firstToolSeen=${fs}, finishReason=${streamFinishReason}, rawFinishReason=${streamRawFinishReason}`,
|
|
419
|
+
`[RUNNER] Stream finished. finishSeen=${_finishObserved}, firstToolSeen=${fs}, trailingAssistantTextAfterTool=${_trailingAssistantTextAfterTool}, finishReason=${streamFinishReason}, rawFinishReason=${streamRawFinishReason}`,
|
|
408
420
|
);
|
|
409
421
|
|
|
410
422
|
const MAX_CONTINUATIONS = 6;
|
|
@@ -418,6 +430,7 @@ async function runAssistant(opts: RunOpts) {
|
|
|
418
430
|
finishReason: streamFinishReason,
|
|
419
431
|
rawFinishReason: streamRawFinishReason,
|
|
420
432
|
firstToolSeen: fs,
|
|
433
|
+
hasTrailingAssistantText: _trailingAssistantTextAfterTool,
|
|
421
434
|
droppedPseudoToolText: oauthTextGuard?.dropped ?? false,
|
|
422
435
|
lastAssistantText: latestAssistantText,
|
|
423
436
|
});
|