@jvittechs/jai1-cli 0.1.73 → 0.1.75
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/README.md +6 -424
- package/dist/cli.js +31 -18
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,433 +1,15 @@
|
|
|
1
1
|
# @jvittechs/jai1-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **🇻🇳:** Đây là công cụ CLI thống nhất dành cho các developer của JV-IT TECHS để quản lý Jai1 Framework. Vui lòng liên hệ với TeamAI để được hướng dẫn cách sử dụng.
|
|
4
|
+
>
|
|
5
|
+
> **🇯🇵:** これはJV-IT TECHSの開発者がJai1 Frameworkを管理するための統合CLIツールです。使用方法についてはTeamAIにお問い合わせください。
|
|
6
|
+
>
|
|
7
|
+
> **🇬🇧:** A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.
|
|
4
8
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### Jai1 Framework Management
|
|
8
|
-
- 📦 Download and update Jai1 Framework from Jai1 Store
|
|
9
|
-
- 🔄 Sync framework to any project with one command
|
|
10
|
-
- 🔐 Secure access key authentication
|
|
11
|
-
- 📋 Version checking and auto-update support
|
|
12
|
-
|
|
13
|
-
### Redmine Context Sync
|
|
14
|
-
- 🎯 Extract Redmine issues as structured context for AI agents
|
|
15
|
-
- 📝 Sync individual issues or entire projects to local Markdown files
|
|
16
|
-
- ⚡ Incremental updates with change detection
|
|
17
|
-
- 💬 Comment synchronization with tracking
|
|
18
|
-
- 🔧 Configurable filename patterns and slug generation
|
|
19
|
-
- 🚀 Concurrent processing for large projects
|
|
20
|
-
- 🔁 Retry logic and rate limiting
|
|
21
|
-
- ✅ YAML configuration with validation
|
|
22
|
-
|
|
23
|
-
## Installation
|
|
9
|
+
## Cài đặt / インストール / Installation
|
|
24
10
|
|
|
25
11
|
```bash
|
|
26
12
|
npm install -g @jvittechs/jai1-cli
|
|
27
13
|
# or
|
|
28
14
|
pnpm add -g @jvittechs/jai1-cli
|
|
29
15
|
```
|
|
30
|
-
|
|
31
|
-
## Quick Start
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# 1. Initialize Jai1 CLI with your access key
|
|
35
|
-
jai1 init
|
|
36
|
-
|
|
37
|
-
# 2. Download and sync framework to your project
|
|
38
|
-
jai1 update
|
|
39
|
-
|
|
40
|
-
# 3. Setup Redmine config (create redmine.config.yaml)
|
|
41
|
-
# 4. Sync Redmine issues
|
|
42
|
-
jai1 redmine sync project
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Jai1 Framework Commands
|
|
48
|
-
|
|
49
|
-
### Apply Components (Install/Update)
|
|
50
|
-
|
|
51
|
-
The `apply` command is the main entry point for installing or updating components. It supports interactive mode, packages, or specific files.
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
# Interactive mode (Browse packages/components)
|
|
55
|
-
jai1 apply
|
|
56
|
-
|
|
57
|
-
# Apply a specific package (e.g., 'core')
|
|
58
|
-
jai1 apply package core
|
|
59
|
-
|
|
60
|
-
# Apply specific components by path
|
|
61
|
-
jai1 apply workflows/commit-it.md
|
|
62
|
-
jai1 apply skills/gen-commit-message
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Features:**
|
|
66
|
-
- **Dependency Resolution**: Automatically installs required dependencies.
|
|
67
|
-
- **Search**: Interactive search for components.
|
|
68
|
-
- **Backup**: Automatically backs up modified files to `.jai1_backup/`.
|
|
69
|
-
|
|
70
|
-
### Check for Updates
|
|
71
|
-
|
|
72
|
-
Check if any installed components have newer versions available in the store.
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
jai1 check
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**Output:**
|
|
79
|
-
```
|
|
80
|
-
Checking 5 components...
|
|
81
|
-
📦 workflows/commit-it.md: v1.0.0 (Latest)
|
|
82
|
-
📦 skills/gen-commit-message: v1.0.0 -> v1.1.0 ⚠️ Update available
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Update All Components
|
|
86
|
-
|
|
87
|
-
Update all currently installed components to their latest versions.
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
jai1 update
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**Output:**
|
|
94
|
-
```
|
|
95
|
-
✅ Updated workflows/commit-it.md (v1.0.1)
|
|
96
|
-
✅ Updated skills/gen-commit-message (v1.1.0)
|
|
97
|
-
Backup created at .jai1_backup/2023-12-10T10-00-00/
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Manage Backups
|
|
101
|
-
|
|
102
|
-
Clear old backups to free up space.
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# Clear all backups
|
|
106
|
-
jai1 clear-backups
|
|
107
|
-
|
|
108
|
-
# Clear backups older than 7 days
|
|
109
|
-
jai1 clear-backups --older-than 7d
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Show Info
|
|
113
|
-
|
|
114
|
-
Display configuration and environment information.
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
jai1 info
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Redmine Commands
|
|
123
|
-
|
|
124
|
-
### Check Connectivity
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
jai1 redmine check
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Output:**
|
|
131
|
-
```
|
|
132
|
-
✓ Redmine API accessible
|
|
133
|
-
✓ Project found: my-project (ID: 123)
|
|
134
|
-
✓ Issues API: 45 issues available
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Sync Single Issue
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
# By ID
|
|
141
|
-
jai1 redmine sync issue --id 123
|
|
142
|
-
|
|
143
|
-
# By URL
|
|
144
|
-
jai1 redmine sync issue --url https://redmine.example.com/issues/123
|
|
145
|
-
|
|
146
|
-
# Dry run (preview changes)
|
|
147
|
-
jai1 redmine sync issue --id 123 --dry-run
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Sync Project
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
# Sync all issues
|
|
154
|
-
jai1 redmine sync project
|
|
155
|
-
|
|
156
|
-
# Filter by status
|
|
157
|
-
jai1 redmine sync project --status "open"
|
|
158
|
-
|
|
159
|
-
# Sync issues updated since date
|
|
160
|
-
jai1 redmine sync project --updated-since 2023-01-01
|
|
161
|
-
|
|
162
|
-
# Custom concurrency and page size
|
|
163
|
-
jai1 redmine sync project --concurrency 8 --page-size 50
|
|
164
|
-
|
|
165
|
-
# Dry run
|
|
166
|
-
jai1 redmine sync project --dry-run
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
**Output:**
|
|
170
|
-
```
|
|
171
|
-
Syncing project: my-project (ID: 123)
|
|
172
|
-
[████████████████████████████████] 45/45 issues
|
|
173
|
-
|
|
174
|
-
Summary:
|
|
175
|
-
Created: 5 issues
|
|
176
|
-
Updated: 12 issues
|
|
177
|
-
Unchanged: 28 issues
|
|
178
|
-
|
|
179
|
-
Output: .jai1/redmine/
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## VSCode Commands
|
|
185
|
-
|
|
186
|
-
Manage VSCode settings for optimal IDE performance with modular optimization groups.
|
|
187
|
-
|
|
188
|
-
### Quick Start
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
# Interactive mode
|
|
192
|
-
jai1 vscode
|
|
193
|
-
|
|
194
|
-
# Max performance (enable all optimizations)
|
|
195
|
-
jai1 vscode max-performance
|
|
196
|
-
|
|
197
|
-
# List available groups
|
|
198
|
-
jai1 vscode list
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Available Commands
|
|
202
|
-
|
|
203
|
-
| Command | Description |
|
|
204
|
-
|---------|-------------|
|
|
205
|
-
| `jai1 vscode` | Interactive mode to select groups |
|
|
206
|
-
| `jai1 vscode enable [groups...]` | Enable specific optimization groups |
|
|
207
|
-
| `jai1 vscode disable [groups...]` | Disable specific optimization groups |
|
|
208
|
-
| `jai1 vscode max-performance` | Enable all optimizations |
|
|
209
|
-
| `jai1 vscode reset [groups...]` | Reset to default settings |
|
|
210
|
-
| `jai1 vscode list` | List all available groups |
|
|
211
|
-
|
|
212
|
-
### Performance Groups
|
|
213
|
-
|
|
214
|
-
| Group | Description | Impact |
|
|
215
|
-
|-------|-------------|--------|
|
|
216
|
-
| `telemetry` | Disable all telemetry and data collection | 🟢 Low |
|
|
217
|
-
| `languageServers` | Disable/reduce language servers (tsserver, eslint...) | 🔴 High |
|
|
218
|
-
| `git` | Disable Git integration | 🟡 Medium |
|
|
219
|
-
| `fileWatcher` | Exclude directories from file watcher | 🟡 Medium |
|
|
220
|
-
| `search` | Exclude directories from search | 🟢 Low |
|
|
221
|
-
| `extensions` | Disable auto-update extensions | 🟢 Low |
|
|
222
|
-
| `editorRendering` | Optimize rendering (minimap, whitespace...) | 🟡 Medium |
|
|
223
|
-
| `uiElements` | Hide unnecessary UI elements | 🟢 Low |
|
|
224
|
-
|
|
225
|
-
### Common Scenarios
|
|
226
|
-
|
|
227
|
-
**Large Project (Node.js/React):**
|
|
228
|
-
```bash
|
|
229
|
-
jai1 vscode enable fileWatcher search extensions
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
**Low-spec Machine:**
|
|
233
|
-
```bash
|
|
234
|
-
jai1 vscode max-performance
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Privacy (Disable Telemetry):**
|
|
238
|
-
```bash
|
|
239
|
-
jai1 vscode enable telemetry
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
**Minimal UI:**
|
|
243
|
-
```bash
|
|
244
|
-
jai1 vscode enable uiElements editorRendering
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
**Restore Features:**
|
|
248
|
-
```bash
|
|
249
|
-
# Restore specific group
|
|
250
|
-
jai1 vscode disable languageServers
|
|
251
|
-
|
|
252
|
-
# Restore all
|
|
253
|
-
jai1 vscode reset
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### Documentation
|
|
257
|
-
|
|
258
|
-
- [Full Documentation](./docs/VSCODE_COMMAND.md) - Detailed guide with all settings
|
|
259
|
-
- [Quick Reference](./docs/VSCODE_QUICK_REF.md) - Quick reference for common use cases
|
|
260
|
-
- [Refactoring Summary](./docs/REFACTOR_VSCODE_COMMAND.md) - Technical details of the refactoring
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Configuration
|
|
265
|
-
|
|
266
|
-
### Global Config: `~/.jai1/config.json`
|
|
267
|
-
|
|
268
|
-
Created by `jai1 init`:
|
|
269
|
-
|
|
270
|
-
```json
|
|
271
|
-
{
|
|
272
|
-
"apiUrl": "https://store.jai1.io",
|
|
273
|
-
"accessKey": "YOUR_ACCESS_KEY",
|
|
274
|
-
"version": "1.2.3",
|
|
275
|
-
"lastUpdated": "2025-12-09T00:00:00Z"
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
### Redmine Config: `redmine.config.yaml`
|
|
280
|
-
|
|
281
|
-
Create this file in your project root:
|
|
282
|
-
|
|
283
|
-
```yaml
|
|
284
|
-
baseUrl: https://redmine.example.com
|
|
285
|
-
apiAccessToken: YOUR_API_TOKEN
|
|
286
|
-
project:
|
|
287
|
-
id: 123
|
|
288
|
-
identifier: my-project
|
|
289
|
-
outputDir: .jai1/redmine
|
|
290
|
-
defaults:
|
|
291
|
-
include: [journals, relations, attachments]
|
|
292
|
-
status: '*'
|
|
293
|
-
pageSize: 100
|
|
294
|
-
concurrency: 4
|
|
295
|
-
retry:
|
|
296
|
-
retries: 3
|
|
297
|
-
baseMs: 300
|
|
298
|
-
filename:
|
|
299
|
-
pattern: '{issueId}-{slug}.md'
|
|
300
|
-
slug:
|
|
301
|
-
maxLength: 80
|
|
302
|
-
dedupe: true
|
|
303
|
-
lowercase: true
|
|
304
|
-
renameOnTitleChange: false
|
|
305
|
-
comments:
|
|
306
|
-
anchors:
|
|
307
|
-
start: '<!-- redmine:comments:start -->'
|
|
308
|
-
end: '<!-- redmine:comments:end -->'
|
|
309
|
-
trackBy: journalId
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Configuration Options
|
|
313
|
-
|
|
314
|
-
| Option | Type | Description | Default |
|
|
315
|
-
|--------|------|-------------|---------|
|
|
316
|
-
| `baseUrl` | string | Redmine instance URL | Required |
|
|
317
|
-
| `apiAccessToken` | string | Redmine API access token | Required |
|
|
318
|
-
| `project.id` | number | Project ID (numeric) | Required |
|
|
319
|
-
| `project.identifier` | string | Project identifier (string) | Required |
|
|
320
|
-
| `outputDir` | string | Directory to store markdown files | `.jai1/redmine` |
|
|
321
|
-
| `defaults.include` | array | What to include (journals, relations, attachments) | `[journals]` |
|
|
322
|
-
| `defaults.status` | string | Filter by status (`*` for all) | `*` |
|
|
323
|
-
| `defaults.pageSize` | number | API page size (1-100) | `100` |
|
|
324
|
-
| `defaults.concurrency` | number | Concurrent requests (1-10) | `4` |
|
|
325
|
-
| `filename.pattern` | string | Filename pattern with placeholders | `{issueId}-{slug}.md` |
|
|
326
|
-
| `comments.trackBy` | string | How to track new comments | `journalId` |
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Global Options
|
|
331
|
-
|
|
332
|
-
| Option | Description |
|
|
333
|
-
|--------|-------------|
|
|
334
|
-
| `-c, --config <path>` | Path to configuration file |
|
|
335
|
-
| `-o, --output-dir <path>` | Output directory for files |
|
|
336
|
-
| `--dry-run` | Show what would be done without making changes |
|
|
337
|
-
| `--json` | Output results as JSON |
|
|
338
|
-
| `--verbose` | Enable verbose logging |
|
|
339
|
-
| `--version` | Show CLI version |
|
|
340
|
-
| `--help` | Show help |
|
|
341
|
-
|
|
342
|
-
---
|
|
343
|
-
|
|
344
|
-
## Output Format
|
|
345
|
-
|
|
346
|
-
Redmine issues are saved as Markdown files with frontmatter:
|
|
347
|
-
|
|
348
|
-
```markdown
|
|
349
|
-
---
|
|
350
|
-
id: 123
|
|
351
|
-
subject: 'Example Issue'
|
|
352
|
-
status: 'Open'
|
|
353
|
-
priority: 'Normal'
|
|
354
|
-
author: 'John Doe'
|
|
355
|
-
assigned_to: 'Jane Smith'
|
|
356
|
-
created_on: '2023-01-01T10:00:00Z'
|
|
357
|
-
updated_on: '2023-01-02T15:30:00Z'
|
|
358
|
-
project: 'My Project'
|
|
359
|
-
tracker: 'Bug'
|
|
360
|
-
lastJournalId: 456
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
# Issue Description
|
|
364
|
-
|
|
365
|
-
This is the issue description...
|
|
366
|
-
|
|
367
|
-
<!-- redmine:comments:start -->
|
|
368
|
-
|
|
369
|
-
## John Doe - 2023-01-02 15:30
|
|
370
|
-
|
|
371
|
-
This is a comment on the issue.
|
|
372
|
-
|
|
373
|
-
---
|
|
374
|
-
|
|
375
|
-
## Jane Smith - 2023-01-03 09:15
|
|
376
|
-
|
|
377
|
-
Another comment with changes.
|
|
378
|
-
|
|
379
|
-
**Changes:**
|
|
380
|
-
|
|
381
|
-
- status: Open → In Progress
|
|
382
|
-
- assigned_to: John Doe → Jane Smith
|
|
383
|
-
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
<!-- redmine:comments:end -->
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## Development
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
# Install dependencies
|
|
395
|
-
pnpm install
|
|
396
|
-
|
|
397
|
-
# Run in development mode
|
|
398
|
-
pnpm dev
|
|
399
|
-
|
|
400
|
-
# Build
|
|
401
|
-
pnpm build
|
|
402
|
-
|
|
403
|
-
# Run tests
|
|
404
|
-
pnpm test
|
|
405
|
-
|
|
406
|
-
# Lint
|
|
407
|
-
pnpm lint
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
---
|
|
411
|
-
|
|
412
|
-
## Exit Codes
|
|
413
|
-
|
|
414
|
-
| Code | Description |
|
|
415
|
-
|------|-------------|
|
|
416
|
-
| `0` | Success |
|
|
417
|
-
| `1` | General error |
|
|
418
|
-
| `2` | Validation error (configuration, arguments) |
|
|
419
|
-
| `3` | Authentication error (invalid access key) |
|
|
420
|
-
| `4` | Resource not found (issue, project) |
|
|
421
|
-
| `5` | System/network error |
|
|
422
|
-
|
|
423
|
-
---
|
|
424
|
-
|
|
425
|
-
## Documentation
|
|
426
|
-
|
|
427
|
-
- [PRD (Product Requirements Document)](./docs/PRD-jai1-client.md)
|
|
428
|
-
|
|
429
|
-
---
|
|
430
|
-
|
|
431
|
-
## License
|
|
432
|
-
|
|
433
|
-
MIT
|
package/dist/cli.js
CHANGED
|
@@ -33,8 +33,8 @@ var NetworkError = class extends Jai1Error {
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@jvittechs/jai1-cli",
|
|
36
|
-
version: "0.1.
|
|
37
|
-
description: "
|
|
36
|
+
version: "0.1.75",
|
|
37
|
+
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
|
|
38
38
|
type: "module",
|
|
39
39
|
bin: {
|
|
40
40
|
jai1: "dist/cli.js"
|
|
@@ -1451,7 +1451,7 @@ var IDEOverviewView = ({
|
|
|
1451
1451
|
}
|
|
1452
1452
|
}, { isActive: true });
|
|
1453
1453
|
const currentTab = tabs[selectedTabIndex];
|
|
1454
|
-
const currentItems = currentTab ? ideContext
|
|
1454
|
+
const currentItems = currentTab ? ideContext?.items?.filter((item) => item.type === currentTab.type) ?? [] : [];
|
|
1455
1455
|
const formatSize = (bytes) => {
|
|
1456
1456
|
if (bytes < 1024) return `${bytes}B`;
|
|
1457
1457
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
@@ -1998,7 +1998,7 @@ var ContextApp = ({ initialIDE, initialType, onExit }) => {
|
|
|
1998
1998
|
setScrollPosition(0);
|
|
1999
1999
|
};
|
|
2000
2000
|
const currentIDEContext = ideContexts.find((ctx) => ctx.ide === selectedIDE);
|
|
2001
|
-
const currentItems = currentIDEContext?.items
|
|
2001
|
+
const currentItems = currentIDEContext?.items?.filter((item) => item.type === selectedType) ?? [];
|
|
2002
2002
|
const renderContent = () => {
|
|
2003
2003
|
if (loading) {
|
|
2004
2004
|
return /* @__PURE__ */ React9.createElement(Box7, { padding: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "\u0110ang qu\xE9t context..."));
|
|
@@ -3742,7 +3742,8 @@ var LlmProxyService = class {
|
|
|
3742
3742
|
if (!response.ok) {
|
|
3743
3743
|
throw new Error(`Failed to fetch models: ${response.statusText}`);
|
|
3744
3744
|
}
|
|
3745
|
-
|
|
3745
|
+
const result = await response.json();
|
|
3746
|
+
return result ?? { data: [] };
|
|
3746
3747
|
}
|
|
3747
3748
|
/**
|
|
3748
3749
|
* Fetch user limits
|
|
@@ -3757,7 +3758,7 @@ var LlmProxyService = class {
|
|
|
3757
3758
|
throw new Error(`Failed to fetch limits: ${response.statusText}`);
|
|
3758
3759
|
}
|
|
3759
3760
|
const result = await response.json();
|
|
3760
|
-
return result
|
|
3761
|
+
return result?.data ?? { allowedModels: [], rateLimits: {} };
|
|
3761
3762
|
}
|
|
3762
3763
|
/**
|
|
3763
3764
|
* Fetch usage statistics
|
|
@@ -3771,7 +3772,8 @@ var LlmProxyService = class {
|
|
|
3771
3772
|
if (!response.ok) {
|
|
3772
3773
|
throw new Error(`Failed to fetch usage: ${response.statusText}`);
|
|
3773
3774
|
}
|
|
3774
|
-
|
|
3775
|
+
const result = await response.json();
|
|
3776
|
+
return result ?? { data: [] };
|
|
3775
3777
|
}
|
|
3776
3778
|
/**
|
|
3777
3779
|
* Get models with usage information
|
|
@@ -3784,12 +3786,14 @@ var LlmProxyService = class {
|
|
|
3784
3786
|
// Today only
|
|
3785
3787
|
]);
|
|
3786
3788
|
const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA", { timeZone: "Asia/Ho_Chi_Minh" });
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
const
|
|
3790
|
-
const
|
|
3789
|
+
const modelsData = modelsRes.data ?? [];
|
|
3790
|
+
return modelsData.map((model) => {
|
|
3791
|
+
const allowed = limits.allowedModels?.includes(model.id) ?? false;
|
|
3792
|
+
const dailyLimit = limits.rateLimits?.[model.id] ?? limits.rateLimits?.[model.id.toLowerCase()];
|
|
3793
|
+
const usageData = usage.data ?? [];
|
|
3794
|
+
const usageRecord = usageData.find(
|
|
3791
3795
|
(u) => u.model === model.id && u.date === today
|
|
3792
|
-
) ||
|
|
3796
|
+
) || usageData.find(
|
|
3793
3797
|
(u) => u.model.toLowerCase() === model.id.toLowerCase() && u.date === today
|
|
3794
3798
|
);
|
|
3795
3799
|
const usedToday = usageRecord?.count || 0;
|
|
@@ -4031,7 +4035,7 @@ function useLlmApi(service) {
|
|
|
4031
4035
|
}
|
|
4032
4036
|
}, []);
|
|
4033
4037
|
const refetch = useCallback2(() => {
|
|
4034
|
-
fetchData(
|
|
4038
|
+
fetchData(true);
|
|
4035
4039
|
}, [fetchData]);
|
|
4036
4040
|
return {
|
|
4037
4041
|
...state,
|
|
@@ -4218,9 +4222,13 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4218
4222
|
const [slashMenuIndex, setSlashMenuIndex] = useState13(0);
|
|
4219
4223
|
const [inputValue, setInputValue] = useState13("");
|
|
4220
4224
|
const [selectedModel, setSelectedModel] = useState13("");
|
|
4221
|
-
const { models, loading, refetch } = useLlmApi(service);
|
|
4225
|
+
const { models, loading, error, refetch } = useLlmApi(service);
|
|
4222
4226
|
const { messages, isStreaming, sendMessage } = useChat(service);
|
|
4223
4227
|
useEffect5(() => {
|
|
4228
|
+
if (error && !loading) {
|
|
4229
|
+
setCurrentView("error");
|
|
4230
|
+
return;
|
|
4231
|
+
}
|
|
4224
4232
|
if (models.length > 0 && !selectedModel) {
|
|
4225
4233
|
const first = models.find((m) => m.allowed);
|
|
4226
4234
|
if (first) {
|
|
@@ -4228,7 +4236,7 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4228
4236
|
setCurrentView("chat");
|
|
4229
4237
|
}
|
|
4230
4238
|
}
|
|
4231
|
-
}, [models, selectedModel, initialModel]);
|
|
4239
|
+
}, [models, selectedModel, initialModel, error, loading]);
|
|
4232
4240
|
const filteredCommands = useMemo2(() => {
|
|
4233
4241
|
if (!inputValue.startsWith("/")) return SLASH_COMMANDS;
|
|
4234
4242
|
const filter = inputValue.slice(1).toLowerCase();
|
|
@@ -4280,13 +4288,18 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4280
4288
|
if (showSlashMenu) {
|
|
4281
4289
|
setShowSlashMenu(false);
|
|
4282
4290
|
setInputValue("");
|
|
4283
|
-
} else if (currentView !== "chat" && currentView !== "loading") {
|
|
4291
|
+
} else if (currentView !== "chat" && currentView !== "loading" && currentView !== "error") {
|
|
4284
4292
|
setCurrentView("chat");
|
|
4285
4293
|
} else {
|
|
4286
4294
|
exit();
|
|
4287
4295
|
}
|
|
4288
4296
|
return;
|
|
4289
4297
|
}
|
|
4298
|
+
if (currentView === "error" && (key.return || input === "r")) {
|
|
4299
|
+
setCurrentView("loading");
|
|
4300
|
+
refetch();
|
|
4301
|
+
return;
|
|
4302
|
+
}
|
|
4290
4303
|
if (key.tab && currentView === "chat" && !showSlashMenu && !isStreaming) {
|
|
4291
4304
|
cycleModel();
|
|
4292
4305
|
}
|
|
@@ -4323,8 +4336,8 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4323
4336
|
return /* @__PURE__ */ React25.createElement(Box16, { key: m.id, flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text17, { color: m.id === selectedModel ? "yellow" : "white" }, m.id === selectedModel ? "\u25BA " : " ", m.id), /* @__PURE__ */ React25.createElement(Text17, { color: pct > 80 ? "red" : pct > 50 ? "yellow" : "green" }, " ", bar, " ", /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, used, "/", limit)));
|
|
4324
4337
|
}), /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, "[Esc] Back"));
|
|
4325
4338
|
}, [models, selectedModel]);
|
|
4326
|
-
const footer = currentView === "model" || currentView === "stats" ? "[\u2191\u2193] Navigate \u2022 [Enter] Select \u2022 [Esc] Back" : showSlashMenu ? "[\u2191\u2193] Navigate \u2022 [Enter] Select \u2022 [Esc] Cancel" : "[Tab] Next Model \u2022 [/] Commands \u2022 [Esc] Quit";
|
|
4327
|
-
return /* @__PURE__ */ React25.createElement(Box16, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Box16, { borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text17, { bold: true, color: "magenta" }, "\u{1F916} Jai1 Chat"), /* @__PURE__ */ React25.createElement(Box16, { flexGrow: 1 }), /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, "Model: "), /* @__PURE__ */ React25.createElement(Text17, { color: "yellow", bold: true }, selectedModel || "...")), currentView === "loading" ? /* @__PURE__ */ React25.createElement(Box16, { padding: 2, minHeight: contentHeight.current, alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React25.createElement(Text17, null, /* @__PURE__ */ React25.createElement(Spinner4, { type: "dots" }), " Loading...")) : currentView === "model" ? /* @__PURE__ */ React25.createElement(Box16, { padding: 1, minHeight: contentHeight.current, justifyContent: "center", alignItems: "center" }, /* @__PURE__ */ React25.createElement(
|
|
4339
|
+
const footer = currentView === "error" ? "[Enter/r] Retry \u2022 [Esc] Quit" : currentView === "model" || currentView === "stats" ? "[\u2191\u2193] Navigate \u2022 [Enter] Select \u2022 [Esc] Back" : showSlashMenu ? "[\u2191\u2193] Navigate \u2022 [Enter] Select \u2022 [Esc] Cancel" : "[Tab] Next Model \u2022 [/] Commands \u2022 [Esc] Quit";
|
|
4340
|
+
return /* @__PURE__ */ React25.createElement(Box16, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Box16, { borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text17, { bold: true, color: "magenta" }, "\u{1F916} Jai1 Chat"), /* @__PURE__ */ React25.createElement(Box16, { flexGrow: 1 }), /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, "Model: "), /* @__PURE__ */ React25.createElement(Text17, { color: "yellow", bold: true }, selectedModel || "...")), currentView === "loading" ? /* @__PURE__ */ React25.createElement(Box16, { padding: 2, minHeight: contentHeight.current, alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React25.createElement(Text17, null, /* @__PURE__ */ React25.createElement(Spinner4, { type: "dots" }), " Loading...")) : currentView === "error" ? /* @__PURE__ */ React25.createElement(Box16, { padding: 2, minHeight: contentHeight.current, alignItems: "center", justifyContent: "center", flexDirection: "column" }, /* @__PURE__ */ React25.createElement(Text17, { color: "red", bold: true }, "Connection Error"), /* @__PURE__ */ React25.createElement(Text17, { color: "red" }, error), /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, " "), /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, "Press Enter or 'r' to retry")) : currentView === "model" ? /* @__PURE__ */ React25.createElement(Box16, { padding: 1, minHeight: contentHeight.current, justifyContent: "center", alignItems: "center" }, /* @__PURE__ */ React25.createElement(
|
|
4328
4341
|
ModelSelector,
|
|
4329
4342
|
{
|
|
4330
4343
|
models,
|