vg-coder-cli 2.0.46 → 2.0.47

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/INTEGRATION.md ADDED
@@ -0,0 +1,418 @@
1
+ # VG Coder — Remote Task API Integration Guide
2
+
3
+ Hướng dẫn tích hợp service ngoài để gửi task chat AI Studio và nhận kết quả qua local server VG Coder.
4
+
5
+ ## Kiến trúc
6
+
7
+ ```
8
+ External Service ──HTTP──▶ POST /api/tasks (multipart)
9
+ │ enqueue, persist .vg/tasks/<id>/task.json
10
+
11
+ TaskQueue (FIFO)
12
+ │ load-balance → idle worker
13
+
14
+ Socket.IO task:execute ──▶ Browser tab(s) on aistudio.google.com
15
+ │ AIChat.send({prompt, files})
16
+ │ copyLastTurnAsMarkdown()
17
+
18
+ Socket.IO task:complete ◀──── result
19
+ │ persist result.md, fire webhook
20
+
21
+ Launcher recycle ───▶ chrome.tabs.remove + create
22
+ │ fresh tab w/ ?model=<id>
23
+ ▼ next task gets clean chat
24
+ External Service ◀── GET /api/tasks/:id (polling)
25
+ External Service ◀── POST <webhookUrl> (push, retry 3×)
26
+ ```
27
+
28
+ - **Server**: `http://127.0.0.1:6868` (localhost-only, không auth).
29
+ - **Workers**: mỗi tab Chrome trên `aistudio.google.com` với extension VG Coder = 1 worker. Server tự load-balance song song giữa các worker idle.
30
+ - **Launchers**: mỗi Chrome profile = 1 launcher (background service worker của extension). Dùng để mở/đóng tab AI Studio và lock model cho từng task.
31
+ - **Auto-recycle**: sau mỗi task done/failed, server gọi launcher đóng tab worker đó + mở lại tab mới với `?model=<target>` — guarantee model lock per task. Mark worker `recycling` để tránh race.
32
+ - **Failover**: rate-limit / quota error → mark worker `rate_limited` 30 phút, requeue task sang worker khác (max 3 attempts). Tab giữ nguyên qua cooldown (không recycle khi rate-limited).
33
+ - **Default model**: `gemini-3-flash-preview` (free + multimodal — verified working với image + PDF). Avoid `-lite` (text-only) hoặc `-pro-preview` (paid alias to Deep Research).
34
+ - **Persistence**: tasks lưu vào `.vg/tasks/<id>/{task.json,result.md,files/}` của active project.
35
+
36
+ ## Setup chạy
37
+
38
+ ### Server
39
+
40
+ ```bash
41
+ npm install -g vg-coder-cli # hoặc: cd repo && npm run build
42
+ vg start # khởi động server :6868
43
+ ```
44
+
45
+ ### Worker (browser tab)
46
+
47
+ Mỗi Google account muốn dùng → 1 Chrome profile riêng:
48
+
49
+ 1. Chrome → user menu góc trên-phải → **Add → Person** → đặt tên (ví dụ "Account 2") → Continue.
50
+ 2. Cửa sổ mới: login Google account đó.
51
+ 3. `chrome://extensions` → bật **Developer mode** → **Load unpacked** → chọn:
52
+ ```
53
+ /Users/<you>/.nvm/.../node_modules/vg-coder-cli/src/server/views/vg-coder
54
+ ```
55
+ (Có thể dùng đường dẫn local repo nếu dev: `<repo>/src/server/views/vg-coder`.)
56
+ 4. Truy cập `https://aistudio.google.com`.
57
+ 5. Click **Allow** trên popup `Access other apps and services on this device` (Chrome 130+ Private Network Access — chỉ hiện 1 lần / profile).
58
+
59
+ > Worker tự register sau ~3-5s. Email được scrape từ DOM AI Studio. Verify:
60
+ > ```bash
61
+ > curl -s http://127.0.0.1:6868/api/workers | jq
62
+ > ```
63
+
64
+ ## REST API
65
+
66
+ ### POST `/api/tasks` — submit task
67
+
68
+ `Content-Type: multipart/form-data`
69
+
70
+ | Field | Required | Mô tả |
71
+ |---|---|---|
72
+ | `prompt` | yes (hoặc files) | Text gửi cho AI |
73
+ | `files[]` | no | File đính kèm (max 50 MB / file, max 10 file). PDF, ảnh, audio, video — AI Studio không nhận text |
74
+ | `webhookUrl` | no | URL nhận callback khi task xong |
75
+ | `meta` | no | JSON string, đính kèm metadata tùy ý (forward nguyên về webhook) |
76
+ | `workerLabel` | no | Email worker để pin task vào tài khoản cụ thể (vd `alice@gmail.com`) |
77
+
78
+ **Response 202**
79
+
80
+ ```json
81
+ {
82
+ "taskId": "t_1778250830243_8630f8",
83
+ "status": "queued",
84
+ "position": 1,
85
+ "pollUrl": "/api/tasks/t_1778250830243_8630f8",
86
+ "createdAt": 1778250830243
87
+ }
88
+ ```
89
+
90
+ **Errors**: 400 (thiếu prompt + files), 413 (file > 50 MB), 500 (server error).
91
+
92
+ ### GET `/api/tasks/:id` — poll status
93
+
94
+ ```json
95
+ {
96
+ "id": "t_...",
97
+ "status": "queued|running|done|failed|canceled",
98
+ "prompt": "...",
99
+ "meta": { ... },
100
+ "files": [{ "idx": 0, "name": "...", "mime": "...", "size": 1234 }],
101
+ "webhookUrl": "https://...",
102
+ "webhook": { "attempts": [{ "at": 0, "status": 200 }], "deliveredAt": 0 },
103
+ "worker": { "socketId": "...", "email": "alice@gmail.com", "chatId": "..." },
104
+ "workerLabel": null,
105
+ "attempts": [
106
+ { "workerEmail": "alice@gmail.com", "code": "rate_limit_exceeded", "at": 0 }
107
+ ],
108
+ "result": { "chatId": "...", "markdown": "..." },
109
+ "error": { "code": "...", "message": "..." },
110
+ "timing": { "createdAt": 0, "queuedAt": 0, "startedAt": 0, "finishedAt": 0, "durationMs": 0 },
111
+ "workingDir": "/abs/path"
112
+ }
113
+ ```
114
+
115
+ `result.markdown` chỉ có khi `status="done"`. `attempts[]` ghi mọi lần worker bị rate-limit và task được requeue.
116
+
117
+ ### GET `/api/tasks` — list tasks
118
+
119
+ Query: `?status=done&limit=50`. Trả về summary (`id`, `status`, `prompt[0..200]`, `createdAt`, `durationMs`, `webhookUrl`).
120
+
121
+ ### DELETE `/api/tasks/:id` — cancel
122
+
123
+ - `queued` → status `canceled`, remove khỏi queue.
124
+ - `running` → best-effort: server gửi `task:cancel` cho worker, mark worker idle, status `canceled`, kết quả discard.
125
+ - `done|failed|canceled` → 409.
126
+
127
+ ### GET `/api/tasks/:id/files/:idx` — file blob
128
+
129
+ Worker dùng để fetch file đã upload. Caller thường không gọi.
130
+
131
+ ### GET `/api/workers` — list workers
132
+
133
+ ```json
134
+ {
135
+ "workers": [
136
+ {
137
+ "id": "abc...",
138
+ "email": "alice@gmail.com",
139
+ "status": "idle|busy|rate_limited",
140
+ "currentTaskId": null,
141
+ "cooldownUntil": 1778252081156,
142
+ "lastSeen": 1778250177105,
143
+ "meta": { "domain": "aistudio.google.com", "chatId": "...", "registeredAt": 0 }
144
+ }
145
+ ]
146
+ }
147
+ ```
148
+
149
+ ### GET `/api/launchers` — list profile launchers
150
+
151
+ Mỗi Chrome profile có extension cài → background service worker = 1 launcher kết nối tới server qua socket.io. Launcher dùng để **auto-open AI Studio tab** khi không còn worker nào idle khớp với task (kể cả task pin email).
152
+
153
+ ```json
154
+ {
155
+ "launchers": [
156
+ {
157
+ "id": "X0cKPmkz...",
158
+ "chromeId": "75cd593d-a861-4a6f-89a0-386e1e9e61c5",
159
+ "email": "alice@gmail.com",
160
+ "lastSeen": 1778305286920
161
+ }
162
+ ]
163
+ }
164
+ ```
165
+
166
+ `chromeId` là UUID lưu `chrome.storage.sync` lúc cài extension (per-profile). `email` được bind lúc worker đầu tiên trong profile đó register — sau đó server biết profile nào sign in account nào → routing pin chính xác.
167
+
168
+ Auto-open flow:
169
+ 1. Task `enqueue` + không có worker idle khớp pin → server emit `launcher:open_aistudio` cho launcher có email khớp.
170
+ 2. Launcher SW: `chrome.tabs.query({ url: '*://aistudio.google.com/*' })` — nếu có tab cũ thì focus, không có thì `chrome.tabs.create({ url: '...?model=gemini-3-flash-preview' })`.
171
+ 3. Worker mới register sau ~3-5s → task dispatch.
172
+ 4. Fallback nếu không có launcher: server `open https://aistudio.google.com` qua OS default browser handler (cũ).
173
+
174
+ ### Tab management API (qua launcher)
175
+
176
+ Server có thể chủ động list / đóng / mở tab AI Studio trong từng profile:
177
+
178
+ | Method | Path | Body / Query | Mô tả |
179
+ |---|---|---|---|
180
+ | `GET` | `/api/launcher/tabs` | `?label=<email>` (optional) | List tabs trong profile (hoặc all profiles nếu bỏ label) |
181
+ | `POST` | `/api/launcher/close-tab` | `{ workerLabel?, tabId? }` | Đóng tab cụ thể, hoặc tất cả tab AI Studio nếu bỏ `tabId` |
182
+ | `POST` | `/api/launcher/open-tab` | `{ workerLabel?, model?, url?, active? }` | Mở tab mới. `model` mặc định `gemini-3-flash-preview` |
183
+
184
+ ```bash
185
+ # List tab tất cả profile
186
+ curl -s http://127.0.0.1:6868/api/launcher/tabs | jq
187
+
188
+ # Reset tab profile alice về model multimodal
189
+ curl -X POST -d '{"workerLabel":"alice@gmail.com"}' \
190
+ -H 'Content-Type: application/json' http://127.0.0.1:6868/api/launcher/close-tab
191
+ curl -X POST -d '{"workerLabel":"alice@gmail.com","model":"gemini-3-flash-preview"}' \
192
+ -H 'Content-Type: application/json' http://127.0.0.1:6868/api/launcher/open-tab
193
+ ```
194
+
195
+ ### Modal auto-handling
196
+
197
+ AI Studio thỉnh thoảng pop modal chặn task. Worker tự detect + click button đúng (poll mỗi 400 ms):
198
+
199
+ | Modal heading | Action |
200
+ |---|---|
201
+ | `Start creating with media in Google AI Studio` | Click **Acknowledge** |
202
+ | `Link a paid API key` / `Set up billing` | Click X (close) — fallback model multimodal free khác |
203
+ | `We have updated our Terms of Service` | Click **Dismiss** |
204
+ | Preference voting (`Skip`) | Click Skip |
205
+
206
+ ## Webhook payload
207
+
208
+ Khi task chuyển `done | failed | canceled` và caller có `webhookUrl`, server `POST <url>` với:
209
+
210
+ ```json
211
+ {
212
+ "taskId": "t_...",
213
+ "status": "done",
214
+ "result": { "markdown": "...", "chatId": "..." },
215
+ "error": null,
216
+ "durationMs": 14178,
217
+ "meta": { "your-custom-data": 123 }
218
+ }
219
+ ```
220
+
221
+ - 3 lần retry với delay 1s/4s/16s nếu non-2xx.
222
+ - Failure không làm fail task; xem `task.webhook.attempts[]` để debug.
223
+
224
+ ## Error codes
225
+
226
+ | Code | Khi nào | Failover? |
227
+ |---|---|---|
228
+ | `rate_limit_exceeded` | "You've reached your rate limit" | ✅ retry sang worker khác |
229
+ | `quota_exceeded` | "Failed to generate content: user has exceeded quota" | ✅ |
230
+ | `generation_failed` | Toast "Failed to generate content" khác | ✅ |
231
+ | `model_internal_error` | "An internal error has occurred" | ❌ task fails |
232
+ | `token_count_failed` | `.token-status-error` element | ❌ |
233
+ | `worker_disconnected` | Worker tab đóng giữa chừng | ✅ nếu pin cho phép |
234
+ | `server_restart` | Server restart khi task đang running | ❌ task fails, fire webhook 1 lần |
235
+ | `dispatch_failed` | Lỗi gửi task tới worker | ❌ |
236
+
237
+ Failover chỉ kick khi:
238
+ - Code thuộc nhóm rate-limit/quota
239
+ - `attempts.length < 3`
240
+ - Có worker khác `idle` (và worker đó match `workerLabel` nếu pin)
241
+
242
+ ## Pin to specific account
243
+
244
+ Pin = task chỉ chạy trên worker có email khớp `workerLabel`. Nếu worker đó `rate_limited` → task stay `queued` đến khi cooldown reset (30 phút). Không failover khi pin set.
245
+
246
+ ```bash
247
+ curl -F prompt="Use alice's quota only" \
248
+ -F workerLabel="alice@gmail.com" \
249
+ http://127.0.0.1:6868/api/tasks
250
+ ```
251
+
252
+ ## Examples
253
+
254
+ ### Submit + poll
255
+
256
+ ```bash
257
+ TID=$(curl -s -F prompt="Tóm tắt PDF này 3 gạch đầu dòng" \
258
+ -F files=@spec.pdf \
259
+ http://127.0.0.1:6868/api/tasks \
260
+ | jq -r .taskId)
261
+
262
+ while true; do
263
+ RESP=$(curl -s "http://127.0.0.1:6868/api/tasks/$TID")
264
+ STATUS=$(echo "$RESP" | jq -r .status)
265
+ echo "status=$STATUS"
266
+ case $STATUS in done|failed|canceled) break;; esac
267
+ sleep 2
268
+ done
269
+
270
+ echo "$RESP" | jq -r .result.markdown
271
+ ```
272
+
273
+ ### Submit with webhook
274
+
275
+ ```bash
276
+ curl -F prompt="Phân tích file đính kèm" \
277
+ -F files=@image.png \
278
+ -F webhookUrl="https://your-service.example/vg-callback" \
279
+ -F meta='{"jobId":"abc","userId":42}' \
280
+ http://127.0.0.1:6868/api/tasks
281
+ ```
282
+
283
+ Service nhận POST từ server local → forward lên cloud / write DB.
284
+
285
+ ### Node.js client
286
+
287
+ ```javascript
288
+ async function submitVgTask({ prompt, files = [], webhookUrl, workerLabel, meta }) {
289
+ const fd = new FormData();
290
+ fd.append('prompt', prompt);
291
+ if (webhookUrl) fd.append('webhookUrl', webhookUrl);
292
+ if (workerLabel) fd.append('workerLabel', workerLabel);
293
+ if (meta) fd.append('meta', JSON.stringify(meta));
294
+ for (const f of files) fd.append('files', f.blob, f.name);
295
+
296
+ const r = await fetch('http://127.0.0.1:6868/api/tasks', { method: 'POST', body: fd });
297
+ if (r.status !== 202) throw new Error(`HTTP ${r.status}`);
298
+ return r.json();
299
+ }
300
+
301
+ async function pollVgTask(taskId, { intervalMs = 2000, timeoutMs = 5 * 60_000 } = {}) {
302
+ const start = Date.now();
303
+ while (Date.now() - start < timeoutMs) {
304
+ const r = await fetch(`http://127.0.0.1:6868/api/tasks/${taskId}`);
305
+ const t = await r.json();
306
+ if (['done', 'failed', 'canceled'].includes(t.status)) return t;
307
+ await new Promise(r => setTimeout(r, intervalMs));
308
+ }
309
+ throw new Error('timeout');
310
+ }
311
+ ```
312
+
313
+ ## Debug API
314
+
315
+ Cùng tiếp đầu `/api/worker/...`. Optional query/body `workerLabel` để target worker cụ thể; bỏ qua = worker idle đầu tiên.
316
+
317
+ | Endpoint | Mô tả |
318
+ |---|---|
319
+ | `GET /api/worker/url` | URL/title/chatId/rateLimit hiện tại |
320
+ | `POST /api/worker/probe` | `{selector, kind}` → query DOM, kind = `text\|html\|outer\|attrs` |
321
+ | `POST /api/worker/dom` | `{selector?, maxBytes?}` → outerHTML, truncate nếu > maxBytes (default 200 KB) |
322
+ | `POST /api/worker/eval` | `{code, timeoutMs?}` → eval JS trong page (async được, return value serialize JSON) |
323
+ | `POST /api/worker/logs` | `{since?, level?, limit?, clear?}` → console + window.error + unhandledrejection (ring buffer 200) |
324
+ | `GET /api/worker/screenshot?format=png\|jpeg&quality=NN` | Native viewport capture qua `chrome.tabs.captureVisibleTab` (~250-500 ms). Tab AI Studio phải active. |
325
+
326
+ ```bash
327
+ # Xem worker đang ở chat nào
328
+ curl http://127.0.0.1:6868/api/worker/url?label=alice@gmail.com | jq
329
+
330
+ # Inspect rate-limit text bất kỳ lúc nào
331
+ curl -X POST -H 'Content-Type: application/json' \
332
+ -d '{"selector":".model-error, ms-upgrade-options-callout","kind":"text"}' \
333
+ http://127.0.0.1:6868/api/worker/probe | jq
334
+
335
+ # Eval custom code
336
+ curl -X POST -H 'Content-Type: application/json' \
337
+ -d '{"code":"return document.querySelectorAll(\"ms-chat-turn\").length"}' \
338
+ http://127.0.0.1:6868/api/worker/eval | jq
339
+
340
+ # Capture screenshot worker hiện tại (PNG mặc định)
341
+ curl http://127.0.0.1:6868/api/worker/screenshot -o /tmp/snap.png && open /tmp/snap.png
342
+
343
+ # JPEG nén nhẹ — nhanh hơn, file nhỏ hơn (~50% size)
344
+ curl "http://127.0.0.1:6868/api/worker/screenshot?format=jpeg&quality=60&label=alice@gmail.com" -o /tmp/snap.jpg
345
+
346
+ # Xem console errors gần đây
347
+ curl -X POST -H 'Content-Type: application/json' \
348
+ -d '{"level":"error","limit":20}' \
349
+ http://127.0.0.1:6868/api/worker/logs | jq
350
+
351
+ # Logs từ thời điểm cụ thể (epoch ms) — polling incremental
352
+ curl -X POST -H 'Content-Type: application/json' \
353
+ -d '{"since":1778300000000,"limit":50}' \
354
+ http://127.0.0.1:6868/api/worker/logs | jq '.logs[] | {at, level, message}'
355
+ ```
356
+
357
+ ## Known limitations
358
+
359
+ - **PNA prompt** lần đầu / profile: Chrome 130+ bắt user click Allow. Server-side `Access-Control-Allow-Private-Network` header có nhưng Chrome vẫn show prompt; click 1 lần → permanent.
360
+ - **AI Studio file types**: chỉ chấp nhận PDF / ảnh / audio / video. File text/markdown/code thường fail upload.
361
+ - **Chat reset per task = recycle tab**: mỗi task xong server đóng tab + mở tab mới (`?model=<target>`). Tab disconnect ~3-5s giữa các task. Không "continue same chat".
362
+ - **Single quota per account/day**: rate-limit auto-failover chỉ chuyển sang account còn quota.
363
+ - **AI Studio đôi khi treo silent**: turn không render sau Run. Worker timeout default 5 phút sẽ đẩy task về `failed`.
364
+ - **Tab AI Studio phải visible + active trong khi task chạy** — xem mục _Tab visibility constraint_ bên dưới.
365
+ - **Model selection bị Angular routerLink ignore href**: AI Studio's "+ New chat" link không preserve `?model=` query → session drift sang Deep Research Preview. Workaround = recycle tab per task (đã implement).
366
+ - **Binary file upload**: phải dùng direct fetch + Blob, KHÔNG qua extension proxy fetch (`vetgoFetch arrayBuffer`) vì chrome.runtime structured-clone corrupt ArrayBuffer (verify SHA-256 mismatch).
367
+
368
+ ## Tab visibility constraint
369
+
370
+ Hệ thống dùng cơ chế "Copy as markdown" của AI Studio để lấy kết quả task — cụ thể: mở menu ⋮ trên turn cuối → click "Copy as markdown" → AI Studio gọi `document.execCommand('copy')` → script hook bắt nội dung. Đây là path duy nhất lấy được markdown chuẩn (DOM scrape không lossless cho code block / table / latex).
371
+
372
+ Chrome enforce 2 điều kiện ở C++ layer cho `execCommand('copy')`, **không thể bypass bằng API hay flag**:
373
+
374
+ 1. Tab phải là **active tab** trong window của nó (`document.visibilityState === 'visible'`).
375
+ 2. Window chứa tab phải **không bị occlude** (không minimize, không bị app khác fullscreen che kín).
376
+
377
+ Hệ quả: trong khi 1 task đang chạy (~10-15s) trên 1 worker tab, **user không được**:
378
+
379
+ - Click sang tab khác trong cùng window AI Studio.
380
+ - Minimize window đó (Cmd+M / nút vàng).
381
+ - Để app khác fullscreen che kín window đó (đặc biệt trên macOS — strict occlusion detection).
382
+
383
+ User vẫn có thể:
384
+
385
+ - Làm việc ở **window Chrome khác** hoặc **app khác** (Cmd+Tab) — chỉ cần window AI Studio không bị che kín. Tab vẫn `visible` vì là active trong window của nó.
386
+ - Đặt window AI Studio trên **monitor thứ 2** — luôn visible, không vướng workflow chính.
387
+ - Đóng tab giữa các task (không đụng vào trong khi task chạy) — task tiếp theo launcher tự mở lại.
388
+
389
+ Đã thử các approach để bypass và đều fail:
390
+
391
+ | Approach | Vấn đề |
392
+ |---|---|
393
+ | `chrome.windows.create({ state: 'minimized' })` | `visibilityState='hidden'` → clipboard fail |
394
+ | `state: 'normal' + focused: false` | macOS occlusion → vẫn `hidden` nếu bị app khác che |
395
+ | Tab group collapsed | Tab bị treat như background tab → `hidden` |
396
+ | `Page.bringToFront` (CDP) | Không override OS-level occlusion |
397
+ | DOM scrape thay clipboard | Mất format code/latex/table → không acceptable |
398
+
399
+ **Trade-off**: muốn task chạy 100% reliable thì user cần tuân thủ điều kiện trên. Nếu chấp nhận task fail thỉnh thoảng (do user vô tình minimize hoặc occlude), có thể bỏ qua — task sẽ failover sang worker khác hoặc retry.
400
+
401
+ ## Health check
402
+
403
+ ```bash
404
+ curl http://127.0.0.1:6868/health
405
+ # {"status":"ok","version":"2.0.49"}
406
+ ```
407
+
408
+ ## End-to-end smoke test
409
+
410
+ ```bash
411
+ # T1-T5: submit, file+webhook, cancel, list workers, pin
412
+ node tests/remote-task-e2e.js
413
+
414
+ # D1-D4: debug API + file upload (logs, screenshot, image, PDF)
415
+ node tests/debug-and-files-test.js
416
+ ```
417
+
418
+ `debug-and-files-test.js` ghi log ra `/tmp/vg-test-log.txt`, screenshot ra `/tmp/vg-test-screenshot.{png,jpg}`, kết quả markdown ra `/tmp/vg-test-{image,pdf}-result.md`. Cần ≥1 worker idle với email thật + file fixture `tests/feline-xray-chest7.jpg` + `tests/REMOTE-SIGNING.pdf`. Verified: 4/4 tests pass trong ~87s.
package/debug.log ADDED
@@ -0,0 +1,42 @@
1
+ - Initializing analysis...
2
+
3
+ 📁 Project Detection:
4
+ Primary Type: react
5
+ Other Types: nodejs
6
+
7
+ 📊 Scan Results:
8
+ Files Found: 3
9
+ Files Processed: 3
10
+ Scan Time: 13ms
11
+
12
+ 📁 Directory Structure:
13
+ test-large/
14
+ ├── src
15
+ │ ├── components
16
+ │ │ └── Button.tsx (.tsx)
17
+ │ └── index.ts (.ts)
18
+ └── package.json (.json)
19
+
20
+
21
+ 🧮 Token Analysis:
22
+ Total Tokens: 121
23
+ Average Tokens/File: 40
24
+ Files Exceeding Limit: 0
25
+ Estimated Chunks: 1
26
+ DEBUG - aiContent length: 322
27
+ DEBUG - first 200 chars: // ===== FILE: package.json =====
28
+ {"name": "test-large", "version": "1.0.0", "dependencies": {"react": "^18.0.0"}}
29
+
30
+ // ===== FILE: src/components/Button.tsx =====
31
+ export const Button = () => <button>C
32
+ ✔ Content saved to vg-projects.txt successfully!
33
+
34
+ 📄 File Content:
35
+ Output: /Users/thanhhuynh/Documents/vg-coder-cli/vg-projects.txt
36
+ Files: 3
37
+ Lines: 9
38
+ Characters: 322
39
+ Size: 322 Bytes
40
+
41
+ 💡 Ready for AI analysis!
42
+ Content is now saved in vg-projects.txt and ready for AI tools.