@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.
Files changed (112) hide show
  1. package/dist/assets/index-D3jfzuKN.css +32 -0
  2. package/dist/assets/{index-eGqPeIEu.js → index-DTMWjJj8.js} +324 -305
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/modules/commands/commands.routes.js +3 -3
  5. package/dist-server/server/modules/commands/commands.routes.js.map +1 -1
  6. package/dist-server/server/modules/commands/tests/commands.test.js +2 -7
  7. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -1
  8. package/dist-server/server/modules/database/index.js +2 -0
  9. package/dist-server/server/modules/database/index.js.map +1 -1
  10. package/dist-server/server/modules/database/migrations.js +18 -1
  11. package/dist-server/server/modules/database/migrations.js.map +1 -1
  12. package/dist-server/server/modules/database/repositories/provider-models.js +112 -0
  13. package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -0
  14. package/dist-server/server/modules/database/repositories/sessions.db.js +63 -7
  15. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  16. package/dist-server/server/modules/database/schema.js +28 -3
  17. package/dist-server/server/modules/database/schema.js.map +1 -1
  18. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +102 -0
  19. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -0
  20. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -1
  21. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
  22. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +22 -0
  23. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  24. package/dist-server/server/modules/file-tree/file-tree.module.js +5 -1
  25. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
  26. package/dist-server/server/modules/file-tree/file-tree.service.js +50 -18
  27. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
  28. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +83 -7
  29. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
  30. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +46 -14
  31. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  32. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +8 -15
  33. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
  34. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +58 -71
  35. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  36. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +1 -54
  37. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  38. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +7 -7
  39. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  40. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +32 -669
  41. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  42. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +105 -328
  43. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  44. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +0 -12
  45. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  46. package/dist-server/server/modules/providers/provider.routes.js +139 -31
  47. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  48. package/dist-server/server/modules/providers/services/provider-models.service.js +167 -199
  49. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  50. package/dist-server/server/modules/providers/services/provider-runtime.service.js +2 -2
  51. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
  52. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +12 -4
  53. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  54. package/dist-server/server/modules/providers/services/sessions.service.js +62 -3
  55. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  56. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +53 -17
  57. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  58. package/dist-server/server/modules/providers/tests/opencode-models.test.js +15 -126
  59. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  60. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +5 -5
  61. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  62. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +175 -292
  63. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  64. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +2 -6
  65. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -1
  66. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +1 -0
  67. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
  68. package/dist-server/server/modules/providers/tests/provider.routes.test.js +126 -0
  69. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -0
  70. package/dist-server/server/modules/providers/tests/sessions.service.test.js +87 -0
  71. package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -0
  72. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +5 -1
  73. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  74. package/package.json +3 -3
  75. package/server/modules/commands/commands.routes.ts +3 -3
  76. package/server/modules/commands/tests/commands.test.ts +2 -7
  77. package/server/modules/database/index.ts +2 -0
  78. package/server/modules/database/migrations.ts +20 -0
  79. package/server/modules/database/repositories/provider-models.ts +168 -0
  80. package/server/modules/database/repositories/sessions.db.ts +85 -8
  81. package/server/modules/database/schema.ts +29 -3
  82. package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -0
  83. package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -1
  84. package/server/modules/database/tests/sessions.db.integration.test.ts +32 -0
  85. package/server/modules/file-tree/file-tree.module.ts +5 -1
  86. package/server/modules/file-tree/file-tree.service.ts +68 -20
  87. package/server/modules/file-tree/tests/file-tree.service.test.ts +104 -7
  88. package/server/modules/providers/list/claude/claude-models.provider.ts +46 -14
  89. package/server/modules/providers/list/claude/claude-runtime.provider.js +8 -16
  90. package/server/modules/providers/list/codex/codex-models.provider.ts +58 -99
  91. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +1 -60
  92. package/server/modules/providers/list/codex/codex-sessions.provider.ts +7 -7
  93. package/server/modules/providers/list/cursor/cursor-models.provider.ts +32 -710
  94. package/server/modules/providers/list/opencode/opencode-models.provider.ts +105 -413
  95. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +0 -13
  96. package/server/modules/providers/provider.routes.ts +185 -33
  97. package/server/modules/providers/services/provider-models.service.ts +223 -288
  98. package/server/modules/providers/services/provider-runtime.service.ts +2 -3
  99. package/server/modules/providers/services/session-conversations-search.service.ts +12 -4
  100. package/server/modules/providers/services/sessions.service.ts +87 -3
  101. package/server/modules/providers/tests/codex-sessions.test.ts +57 -18
  102. package/server/modules/providers/tests/opencode-models.test.ts +34 -135
  103. package/server/modules/providers/tests/opencode-sessions.test.ts +5 -5
  104. package/server/modules/providers/tests/provider-models.service.test.ts +353 -450
  105. package/server/modules/providers/tests/provider-runtime.service.test.ts +2 -6
  106. package/server/modules/providers/tests/provider-token-usage.service.test.ts +1 -0
  107. package/server/modules/providers/tests/provider.routes.test.ts +183 -0
  108. package/server/modules/providers/tests/sessions.service.test.ts +127 -0
  109. package/server/modules/websocket/services/chat-websocket.service.ts +5 -1
  110. package/server/shared/interfaces.ts +4 -6
  111. package/server/shared/types.ts +29 -17
  112. 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 bypassCache = parseOptionalBooleanQuery(req.query.bypassCache, 'bypassCache') ?? false;
389
- const result = await providerModelsService.getProviderModels(provider, { bypassCache });
390
- res.json(createApiSuccessResponse({ provider, models: result.models, cache: result.cache }));
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 result = sessionsService.createAppSession(provider, projectPath);
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 `/sessions/running` and
586
- // `/sessions/archived` routes so those literals never match `:sessionId`.
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 limitRaw = readOptionalQueryString(req.query.limit);
634
- const offsetRaw = readOptionalQueryString(req.query.offset);
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,