@cloudcli-ai/cloudcli 1.37.0 → 1.37.1
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/dist/assets/index-D3jfzuKN.css +32 -0
- package/dist/assets/{index-eGqPeIEu.js → index-DTMWjJj8.js} +324 -305
- package/dist/index.html +2 -2
- package/dist-server/server/modules/commands/commands.routes.js +3 -3
- package/dist-server/server/modules/commands/commands.routes.js.map +1 -1
- package/dist-server/server/modules/commands/tests/commands.test.js +2 -7
- package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -1
- package/dist-server/server/modules/database/index.js +2 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +18 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +112 -0
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +63 -7
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +28 -3
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +102 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -1
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +22 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js +5 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.service.js +50 -18
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +83 -7
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +46 -14
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +8 -15
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +58 -71
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +1 -54
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +7 -7
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +32 -669
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +105 -328
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +0 -12
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +139 -31
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +167 -199
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +2 -2
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +12 -4
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +62 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +53 -17
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +15 -126
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +5 -5
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +175 -292
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +2 -6
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +1 -0
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +126 -0
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +87 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +5 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/package.json +3 -3
- package/server/modules/commands/commands.routes.ts +3 -3
- package/server/modules/commands/tests/commands.test.ts +2 -7
- package/server/modules/database/index.ts +2 -0
- package/server/modules/database/migrations.ts +20 -0
- package/server/modules/database/repositories/provider-models.ts +168 -0
- package/server/modules/database/repositories/sessions.db.ts +85 -8
- package/server/modules/database/schema.ts +29 -3
- package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -0
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -1
- package/server/modules/database/tests/sessions.db.integration.test.ts +32 -0
- package/server/modules/file-tree/file-tree.module.ts +5 -1
- package/server/modules/file-tree/file-tree.service.ts +68 -20
- package/server/modules/file-tree/tests/file-tree.service.test.ts +104 -7
- package/server/modules/providers/list/claude/claude-models.provider.ts +46 -14
- package/server/modules/providers/list/claude/claude-runtime.provider.js +8 -16
- package/server/modules/providers/list/codex/codex-models.provider.ts +58 -99
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +1 -60
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +7 -7
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +32 -710
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +105 -413
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +0 -13
- package/server/modules/providers/provider.routes.ts +185 -33
- package/server/modules/providers/services/provider-models.service.ts +223 -288
- package/server/modules/providers/services/provider-runtime.service.ts +2 -3
- package/server/modules/providers/services/session-conversations-search.service.ts +12 -4
- package/server/modules/providers/services/sessions.service.ts +87 -3
- package/server/modules/providers/tests/codex-sessions.test.ts +57 -18
- package/server/modules/providers/tests/opencode-models.test.ts +34 -135
- package/server/modules/providers/tests/opencode-sessions.test.ts +5 -5
- package/server/modules/providers/tests/provider-models.service.test.ts +353 -450
- package/server/modules/providers/tests/provider-runtime.service.test.ts +2 -6
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +1 -0
- package/server/modules/providers/tests/provider.routes.test.ts +183 -0
- package/server/modules/providers/tests/sessions.service.test.ts +127 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +5 -1
- package/server/shared/interfaces.ts +4 -6
- package/server/shared/types.ts +29 -17
- package/dist/assets/index-DSlds633.css +0 -32
|
@@ -9,6 +9,7 @@ import { providerSkillsService } from '@/modules/providers/services/skills.servi
|
|
|
9
9
|
import { sessionConversationsSearchService } from '@/modules/providers/services/session-conversations-search.service.js';
|
|
10
10
|
import { sessionsService } from '@/modules/providers/services/sessions.service.js';
|
|
11
11
|
import type {
|
|
12
|
+
CustomProviderModelInput,
|
|
12
13
|
LLMProvider,
|
|
13
14
|
McpScope,
|
|
14
15
|
McpTransport,
|
|
@@ -352,6 +353,29 @@ const parseSessionSearchLimit = (value: unknown): number => {
|
|
|
352
353
|
return Math.max(1, Math.min(parsed, 100));
|
|
353
354
|
};
|
|
354
355
|
|
|
356
|
+
const parseBoundedIntegerQuery = <T extends number | null>(
|
|
357
|
+
value: unknown,
|
|
358
|
+
name: string,
|
|
359
|
+
fallback: T,
|
|
360
|
+
minimum: number,
|
|
361
|
+
maximum = Number.MAX_SAFE_INTEGER,
|
|
362
|
+
): number | T => {
|
|
363
|
+
const raw = readOptionalQueryString(value);
|
|
364
|
+
if (raw === undefined) {
|
|
365
|
+
return fallback;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const parsed = Number(raw);
|
|
369
|
+
if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) {
|
|
370
|
+
throw new AppError(`${name} must be an integer between ${minimum} and ${maximum}.`, {
|
|
371
|
+
code: 'INVALID_QUERY_PARAMETER',
|
|
372
|
+
statusCode: 400,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return parsed;
|
|
377
|
+
};
|
|
378
|
+
|
|
355
379
|
const parseSessionModelPayload = (payload: unknown): string => {
|
|
356
380
|
if (!payload || typeof payload !== 'object') {
|
|
357
381
|
throw new AppError('Request body must be an object.', {
|
|
@@ -372,6 +396,92 @@ const parseSessionModelPayload = (payload: unknown): string => {
|
|
|
372
396
|
return model;
|
|
373
397
|
};
|
|
374
398
|
|
|
399
|
+
const parseSessionEffortPayload = (payload: unknown): string => {
|
|
400
|
+
if (!payload || typeof payload !== 'object') {
|
|
401
|
+
throw new AppError('Request body must be an object.', {
|
|
402
|
+
code: 'INVALID_REQUEST_BODY',
|
|
403
|
+
statusCode: 400,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const body = payload as Record<string, unknown>;
|
|
408
|
+
const effort = readOptionalQueryString(body.effort);
|
|
409
|
+
if (!effort) {
|
|
410
|
+
throw new AppError('effort is required.', {
|
|
411
|
+
code: 'EFFORT_REQUIRED',
|
|
412
|
+
statusCode: 400,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (effort.length > 32) {
|
|
417
|
+
throw new AppError('effort must be 32 characters or fewer.', {
|
|
418
|
+
code: 'INVALID_EFFORT',
|
|
419
|
+
statusCode: 400,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return effort;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const parseModelRecordId = (value: unknown): number => {
|
|
427
|
+
const rawRecordId = readPathParam(value, 'recordId').trim();
|
|
428
|
+
if (!/^\d+$/.test(rawRecordId)) {
|
|
429
|
+
throw new AppError('recordId must be a positive integer.', {
|
|
430
|
+
code: 'INVALID_MODEL_RECORD_ID',
|
|
431
|
+
statusCode: 400,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const recordId = Number.parseInt(rawRecordId, 10);
|
|
436
|
+
if (!Number.isSafeInteger(recordId) || recordId < 1) {
|
|
437
|
+
throw new AppError('recordId must be a positive integer.', {
|
|
438
|
+
code: 'INVALID_MODEL_RECORD_ID',
|
|
439
|
+
statusCode: 400,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return recordId;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const parseCustomProviderModelPayload = (payload: unknown): CustomProviderModelInput => {
|
|
447
|
+
if (!payload || typeof payload !== 'object') {
|
|
448
|
+
throw new AppError('Request body must be an object.', {
|
|
449
|
+
code: 'INVALID_REQUEST_BODY',
|
|
450
|
+
statusCode: 400,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const body = payload as Record<string, unknown>;
|
|
455
|
+
const model = readOptionalQueryString(body.model);
|
|
456
|
+
const id = readOptionalQueryString(body.id);
|
|
457
|
+
if (!model) {
|
|
458
|
+
throw new AppError('model is required.', {
|
|
459
|
+
code: 'MODEL_NAME_REQUIRED',
|
|
460
|
+
statusCode: 400,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (!id) {
|
|
464
|
+
throw new AppError('id is required.', {
|
|
465
|
+
code: 'MODEL_ID_REQUIRED',
|
|
466
|
+
statusCode: 400,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (model.length > 80) {
|
|
470
|
+
throw new AppError('model must be 80 characters or fewer.', {
|
|
471
|
+
code: 'MODEL_NAME_TOO_LONG',
|
|
472
|
+
statusCode: 400,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
if (id.length > 200 || /\s/.test(id)) {
|
|
476
|
+
throw new AppError('id must be 200 characters or fewer and cannot contain whitespace.', {
|
|
477
|
+
code: 'INVALID_MODEL_ID',
|
|
478
|
+
statusCode: 400,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return { model, id };
|
|
483
|
+
};
|
|
484
|
+
|
|
375
485
|
router.get(
|
|
376
486
|
'/:provider/auth/status',
|
|
377
487
|
asyncHandler(async (req: Request, res: Response) => {
|
|
@@ -385,9 +495,39 @@ router.get(
|
|
|
385
495
|
'/:provider/models',
|
|
386
496
|
asyncHandler(async (req: Request, res: Response) => {
|
|
387
497
|
const provider = parseProvider(req.params.provider);
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
498
|
+
const models = await providerModelsService.getProviderModels(provider);
|
|
499
|
+
res.json(createApiSuccessResponse({ provider, models }));
|
|
500
|
+
}),
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
router.post(
|
|
504
|
+
'/:provider/models',
|
|
505
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
506
|
+
const provider = parseProvider(req.params.provider);
|
|
507
|
+
const input = parseCustomProviderModelPayload(req.body);
|
|
508
|
+
const result = await providerModelsService.createCustomModel(provider, input);
|
|
509
|
+
res.status(201).json(createApiSuccessResponse({ provider, ...result }));
|
|
510
|
+
}),
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
router.patch(
|
|
514
|
+
'/:provider/models/:recordId',
|
|
515
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
516
|
+
const provider = parseProvider(req.params.provider);
|
|
517
|
+
const recordId = parseModelRecordId(req.params.recordId);
|
|
518
|
+
const input = parseCustomProviderModelPayload(req.body);
|
|
519
|
+
const result = await providerModelsService.updateCustomModel(provider, recordId, input);
|
|
520
|
+
res.json(createApiSuccessResponse({ provider, ...result }));
|
|
521
|
+
}),
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
router.delete(
|
|
525
|
+
'/:provider/models/:recordId',
|
|
526
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
527
|
+
const provider = parseProvider(req.params.provider);
|
|
528
|
+
const recordId = parseModelRecordId(req.params.recordId);
|
|
529
|
+
const result = await providerModelsService.deleteCustomModel(provider, recordId);
|
|
530
|
+
res.json(createApiSuccessResponse({ provider, ...result }));
|
|
391
531
|
}),
|
|
392
532
|
);
|
|
393
533
|
|
|
@@ -420,7 +560,23 @@ router.post(
|
|
|
420
560
|
// A session row only exists once the gateway has allocated one. Report the
|
|
421
561
|
// selection back either way so the client can hold it until the first send.
|
|
422
562
|
res.json(createApiSuccessResponse(
|
|
423
|
-
stored ?? { provider, sessionId, model, source: 'session' as const },
|
|
563
|
+
stored ?? { provider, sessionId, model, effort: null, source: 'session' as const },
|
|
564
|
+
));
|
|
565
|
+
}),
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
/** Records the reasoning-effort choice for one app session. */
|
|
569
|
+
router.post(
|
|
570
|
+
'/:provider/sessions/:sessionId/active-effort',
|
|
571
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
572
|
+
const provider = parseProvider(req.params.provider);
|
|
573
|
+
const sessionId = parseSessionId(req.params.sessionId);
|
|
574
|
+
const effort = parseSessionEffortPayload(req.body);
|
|
575
|
+
const stored = providerModelsService.setSessionEffort(provider, sessionId, effort);
|
|
576
|
+
// Mirror active-model behavior for a composer that picked an effort just
|
|
577
|
+
// before the session gateway created its row.
|
|
578
|
+
res.json(createApiSuccessResponse(
|
|
579
|
+
stored ?? { provider, sessionId, effort, source: 'session' as const },
|
|
424
580
|
));
|
|
425
581
|
}),
|
|
426
582
|
);
|
|
@@ -552,7 +708,8 @@ router.post(
|
|
|
552
708
|
const body = (req.body ?? {}) as Record<string, unknown>;
|
|
553
709
|
const provider = parseProvider(body.provider);
|
|
554
710
|
const projectPath = typeof body.projectPath === 'string' ? body.projectPath : '';
|
|
555
|
-
const
|
|
711
|
+
const initialMessage = typeof body.initialMessage === 'string' ? body.initialMessage : '';
|
|
712
|
+
const result = sessionsService.createAppSession(provider, projectPath, initialMessage);
|
|
556
713
|
res.status(201).json(createApiSuccessResponse(result));
|
|
557
714
|
}),
|
|
558
715
|
);
|
|
@@ -565,6 +722,16 @@ router.get(
|
|
|
565
722
|
}),
|
|
566
723
|
);
|
|
567
724
|
|
|
725
|
+
router.get(
|
|
726
|
+
'/sessions/recent',
|
|
727
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
728
|
+
const limit = parseBoundedIntegerQuery(req.query.limit, 'limit', 40, 1, 100);
|
|
729
|
+
const offset = parseBoundedIntegerQuery(req.query.offset, 'offset', 0, 0);
|
|
730
|
+
const page = sessionsService.listRecentSessions(limit, offset);
|
|
731
|
+
res.json(createApiSuccessResponse(page));
|
|
732
|
+
}),
|
|
733
|
+
);
|
|
734
|
+
|
|
568
735
|
router.get(
|
|
569
736
|
'/sessions/archived',
|
|
570
737
|
asyncHandler(async (_req: Request, res: Response) => {
|
|
@@ -573,6 +740,15 @@ router.get(
|
|
|
573
740
|
}),
|
|
574
741
|
);
|
|
575
742
|
|
|
743
|
+
router.get(
|
|
744
|
+
'/sessions/:sessionId/provider-id',
|
|
745
|
+
asyncHandler(async (req: Request, res: Response) => {
|
|
746
|
+
const sessionId = parseSessionId(req.params.sessionId);
|
|
747
|
+
const providerSessionId = sessionsService.getProviderSessionId(sessionId);
|
|
748
|
+
res.json(createApiSuccessResponse({ sessionId: providerSessionId }));
|
|
749
|
+
}),
|
|
750
|
+
);
|
|
751
|
+
|
|
576
752
|
router.get(
|
|
577
753
|
'/sessions/:sessionId/token-usage',
|
|
578
754
|
asyncHandler(async (req: Request, res: Response) => {
|
|
@@ -582,8 +758,8 @@ router.get(
|
|
|
582
758
|
}),
|
|
583
759
|
);
|
|
584
760
|
|
|
585
|
-
// Must stay registered after the static
|
|
586
|
-
//
|
|
761
|
+
// Must stay registered after the static and session-specific routes so their
|
|
762
|
+
// literals never match the generic `:sessionId` parameter.
|
|
587
763
|
router.get(
|
|
588
764
|
'/sessions/:sessionId',
|
|
589
765
|
asyncHandler(async (req: Request, res: Response) => {
|
|
@@ -630,32 +806,8 @@ router.get(
|
|
|
630
806
|
'/sessions/:sessionId/messages',
|
|
631
807
|
asyncHandler(async (req: Request, res: Response) => {
|
|
632
808
|
const sessionId = parseSessionId(req.params.sessionId);
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
let limit: number | null = null;
|
|
637
|
-
if (limitRaw !== undefined) {
|
|
638
|
-
const parsedLimit = Number.parseInt(limitRaw, 10);
|
|
639
|
-
if (Number.isNaN(parsedLimit) || parsedLimit < 0) {
|
|
640
|
-
throw new AppError('limit must be a non-negative integer.', {
|
|
641
|
-
code: 'INVALID_QUERY_PARAMETER',
|
|
642
|
-
statusCode: 400,
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
limit = parsedLimit;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
let offset = 0;
|
|
649
|
-
if (offsetRaw !== undefined) {
|
|
650
|
-
const parsedOffset = Number.parseInt(offsetRaw, 10);
|
|
651
|
-
if (Number.isNaN(parsedOffset) || parsedOffset < 0) {
|
|
652
|
-
throw new AppError('offset must be a non-negative integer.', {
|
|
653
|
-
code: 'INVALID_QUERY_PARAMETER',
|
|
654
|
-
statusCode: 400,
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
offset = parsedOffset;
|
|
658
|
-
}
|
|
809
|
+
const limit = parseBoundedIntegerQuery(req.query.limit, 'limit', null, 0);
|
|
810
|
+
const offset = parseBoundedIntegerQuery(req.query.offset, 'offset', 0, 0);
|
|
659
811
|
|
|
660
812
|
const result = await sessionsService.fetchHistory(sessionId, {
|
|
661
813
|
limit,
|