codexunlimit 0.1.0-web.12 → 0.1.0-web.15

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 CHANGED
@@ -1,78 +1,96 @@
1
- # Codex Unlimit · Web trên máy
2
-
3
- Cài một gói trên Windows hoặc macOS, mở trình duyệt và bắt đầu sử dụng. Hỗ trợ Node.js 22 từ 22.15.0, hoặc Node.js 24 trở lên.
4
-
5
- ## Cài và mở
6
-
7
- Mở PowerShell (Windows) hoặc Terminal (macOS/Linux). Cài và mở bằng hai dòng:
8
-
9
- ```sh
10
- npm i -g codexunlimit
11
- codexunlimit
12
- ```
13
-
14
- Trang tự mở. Chọn **Cài đặt & hỗ trợ**, nhập mã sử dụng, đăng nhập ChatGPT trong Codex rồi chọn **Kết nối tài khoản**. Có thể chat ngay trên trang. **Bật kết nối với Codex** là tùy chọn để dùng dịch vụ trong ứng dụng Codex.
15
-
16
- Trên Windows, có thể dùng `npm.cmd` và `codexunlimit.cmd` nếu PowerShell chặn file `.ps1`.
17
-
18
- Trang cài đặt công bố nguồn đang có hiệu lực. Khi gói chính thức trên npmjs đã được xác nhận tại đó, có thể dùng `npm i -g codexunlimit` hoặc thêm `@0.1.0-web.8` để cố định phiên bản. URL sản phẩm vẫn là phương án dự phòng. Không coi tên trần trên registry là chính thức trước thông báo xuất bản. Gói không có hook cài đặt tự chạy. Cần Node/npm có sẵn; không tải/chạy PowerShell từ web hay yêu cầu bộ cài .exe riêng. Kiểm tra môi trường bằng `codexunlimit doctor` (hoặc `--json`); lệnh không đọc token ChatGPT.
19
-
20
- ## Ảnh và độ ổn định · web.7
21
-
22
- Công cụ ImageGen gốc của Codex có thể gửi tạo/sửa ảnh qua `/v1/images/generations` và `/v1/images/edits` (cũng hỗ trợ đường dẫn không có `/v1`). Ảnh không đi qua luồng xử lý văn bản/SSE. Tài khoản đã chọn được giữ cho từng request; gateway không tự tạo lại ảnh hoặc đổi tài khoản khi upstream lỗi. Quyền tạo ảnh và quota vẫn do tài khoản upstream quyết định. Đây không phải nút tạo ảnh mới trong chat local và không phải API key OpenAI công khai.
23
-
24
- Lỗi mạng khi gia hạn lease tạm thời trả 503/429, không tự kết luận key hết hạn. Lease còn hạn vẫn được dùng trong khi thử gia hạn; lease hết hạn không được dùng để vượt kiểm tra. Server từ chối/thu hồi thật vẫn có hiệu lực. Phản hồi HTML từ proxy được đổi thành lỗi JSON ngắn, có hướng dẫn chờ, không tự gửi lại request tốn quota.
25
-
26
- Trước khi cập nhật, hoàn tất tác vụ, cài lại bằng lệnh npm đang công bố rồi chạy `codexunlimit restart`. Khởi động lại theo cách này giữ cấu hình kết nối, key/pool/hội thoại. Nếu npm báo file đang được dùng, chọn **Dừng để cập nhật** trong ứng dụng rồi cài lại. Với bản từ URL riêng chưa có công bố npmjs, không dùng `npm update -g codexunlimit` theo tên trần.
27
-
28
- Nếu đã dùng bộ cài quản lý trước đây, không cần cài chồng: dùng `codexunlimit update` của bộ cài đó. Muốn chuyển hẳn sang npm, chuẩn bị Node/npm trước, gỡ bộ cài cũ bằng lệnh `uninstall` của nó (giữ dữ liệu), rồi cài npm. Tránh để hai launcher cùng tên trong PATH khiến mở nhầm phiên bản.
29
-
30
- ## Tài khoản, quota và Markdown
31
-
32
- Mở **Tài khoản & quota** trong trang local để xem mức sử dụng 5 giờ/1 tuần, thời điểm reset và lần cập nhật của từng tài khoản. Nút **Cập nhật quota** đọc hạn mức qua công cụ đăng nhập Codex, không gửi tin nhắn. Phản hồi chat/Codex cũng cập nhật số liệu local và bảng quota admin/khách online hiện có. Nếu chưa có dữ liệu hoặc dữ liệu đã cũ, giao diện ghi rõ; không suy thành 0%.
33
-
34
- Chọn **Thêm tài khoản**, đồng ý sử dụng phiên và mở trang đăng nhập OpenAI. Có thể dùng đăng nhập bằng mã nếu cần. Mỗi tài khoản có phiên riêng, tối đa 10 tài khoản lưu thêm; không thay đổi phiên gốc trong Codex. Sau lần đăng nhập đầu, chọn **Dùng tài khoản này** hoặc chọn ở đầu trang chat. Lựa chọn áp dụng cho yêu cầu mới; yêu cầu đang chạy giữ tài khoản ban đầu. Khi hết quota, ứng dụng báo giới hạn và không tự gửi lại qua tài khoản khác.
35
-
36
- Phiên trong pool được mã hóa bằng AES-GCM với khóa rút từ khóa thiết bị. Trên Windows, khóa thiết bị được bảo vệ bằng DPAPI của người dùng. Công cụ Codex chỉ dùng file phiên tạm trong thư mục riêng khi đăng nhập/làm mới và file được xóa khi tác vụ kết thúc. Token không được trả cho trang web, không ghi log và không gửi về server quản lý; yêu cầu xử lý vẫn gửi access token cần thiết qua gateway như trước. Tài khoản gốc được liên kết chỉ cung cấp access token tạm thời để đọc quota, không sao chép/luân chuyển refresh token của Codex gốc.
37
-
38
- Gói npm tự cài thêm `@openai/codex` 0.166.0 cùng binary đúng hệ điều hành và `markdown-it` 15.0.3. Cần kết nối npm registry khi cài; không dùng `--omit=optional` vì sẽ thiếu binary đăng nhập. Dung lượng tải thực tế bao gồm các dependency, không chỉ file `.tgz` của Codex Unlimit.
39
-
40
- Câu trả lời chat hỗ trợ Markdown khi nhận từng phần và khi mở lại lịch sử: tiêu đề, chữ đậm/nghiêng, danh sách, trích dẫn, bảng, liên kết và khối mã có nút sao chép. HTML thô không được thực thi, liên kết script bị chặn và ảnh bên ngoài không tự tải.
41
-
42
- ## Khay Windows và tắt ứng dụng
43
-
44
- Từ web.5, biểu tượng CU xuất hiện trong khay Windows khi ứng dụng chạy; có thể nằm trong dấu mũi tên lên ở thanh tác vụ. Nhấp đúp để mở trang, nhấp phải để chọn **Mở Codex Unlimit**, **Khởi động cùng Windows**, **Khởi động lại**, hoặc **Tắt ứng dụng**. Tự khởi động mặc định tắt; khi bật, chỉ chạy nền trong khay, không mở trình duyệt.
45
-
46
- **Cài đặt & hỗ trợ → Ứng dụng trên máy** cũng có công tắc khởi động cùng Windows, nút hiện lại biểu tượng khay, hướng dẫn cập nhật và nút tắt. Đóng tab không dừng dịch vụ. **Tắt ứng dụng** hoặc `codexunlimit stop` khôi phục cấu hình Codex do bản này quản lý trước khi dừng, giữ key và dữ liệu. Nếu cấu hình bị bên khác chỉnh sửa, thao tác dừng sẽ báo rõ xung đột thay vì ghi đè.
47
-
48
- `codexunlimit startup on` / `codexunlimit startup off` bật/tắt shortcut trong Startup của tài khoản Windows hiện tại; không cần quyền quản trị. `codexunlimit restart` khởi động lại. macOS/Linux vẫn dùng lệnh mở và tắt; bản này chưa có khay hay tự khởi động cho hai nền tảng đó.
49
-
50
- ## Cập nhật và xử lý kết nối
51
-
52
- Cài gói mới rồi chạy `codexunlimit`: CLI xác minh phiên web đang chạy bằng khóa điều khiển của cùng thư mục dữ liệu, dừng phiên khác phiên bản và mở bản mới. Giữ cấu hình, key và dữ liệu trong lần chuyển này. Không dừng dịch vụ lạ đang chiếm cổng. Với web.1 chưa có giao thức xác minh phiên, chạy `codexunlimit stop` trước. Bản native cũ cần khôi phục kết nối và thoát từ chính bản đó.
53
-
54
- **Dừng để cập nhật** trong phần mở rộng của cài đặt sẽ giữ cấu hình nhưng dừng dịch vụ; Codex tạm thời mất kết nối cho tới khi mở bản mới. Gỡ ứng dụng: tắt **Khởi động cùng Windows**, chọn **Tắt ứng dụng**, rồi chạy `npm uninstall -g codexunlimit`. Dữ liệu riêng vẫn được giữ.
55
-
56
- Khi không kết nối được, chọn **Kiểm tra kết nối**. Trang phân biệt key, tài khoản và cấu hình; profile riêng, cấu hình native, quyền ghi và bản sao khôi phục hỏng có hướng xử lý cụ thể. Nếu Codex đã reset cấu hình về trực tiếp, **Kết nối lại với Codex** tạo lại kết nối và sao lưu cấu hình hiện tại. Không tự sửa cấu hình đang được ứng dụng khác quản lý.
57
-
58
- ## Dữ liệu và tương thích
59
-
60
- - Nền tảng riêng: `https://codexunlimit.tudonghoa.me`, cổng local mặc định `18457`. Không dùng dịch vụ hay thư mục TDH Relay/toolcodex.
61
- - Dữ liệu web ở `%LOCALAPPDATA%/CodexunlimitWeb` trên Windows, `~/Library/Application Support/CodexunlimitWeb` trên macOS. Khóa thiết bị dùng DPAPI trên Windows; file riêng tư theo quyền người dùng trên macOS/Linux.
62
- - Lịch sử chat ở trình duyệt này, tối đa 30 cuộc / 100 tin mỗi cuộc. Khi gửi tin, nội dung và mã truy cập đi qua dịch vụ Codex Unlimit; server không lưu nội dung chat hoặc mã truy cập. Refresh token do Codex quản lý.
63
- - Gói web dùng kích hoạt thiết bị riêng. Nếu chuyển từ native, khôi phục kết nối và dừng bản native trước; quản trị viên có thể giải phóng slot cũ. Không tự chuyển khóa Keychain/DPAPI từ ứng dụng native.
64
- - Chat sử dụng phiên ChatGPT từ Codex; khi hết phiên, đăng nhập lại trong Codex. Chưa có đăng nhập ChatGPT độc lập trong web.
65
- - Không yêu cầu Authenticode hoặc Apple Developer ID để chạy gói Node; xác minh license và chữ ký request vẫn thực hiện. Policy luôn ở server.
66
-
67
- ## Kiểm tra / đóng gói từ repository
68
-
69
- ```sh
70
- npm run start:local
71
- npm run pack:local
72
- npm test
73
- ```
74
-
75
- `pack:local` chỉ đóng gói thư mục connector theo allowlist; không lấy mã server/engine hoặc dữ liệu vận hành. Package đang private để tránh phát hành nhầm lên npm. Sau khi được phân phối, khách có thể cài trực tiếp từ URL gói tải; không cần public server source.
1
+ # Codex Unlimit · Thiết lập nhanh trong terminal
2
+
3
+ Cài một gói trên Windows hoặc macOS, nhập key trong terminal và bắt đầu dùng Codex. Hỗ trợ Node.js 22 từ 22.15.0, hoặc Node.js 24 trở lên.
4
+
5
+ ## Nhập key và áp dụng ngay trong terminal · web.15
6
+
7
+ Lần đầu chạy `codexunlimit` trong PowerShell hoặc Terminal, ứng dụng tự mở luồng thiết lập. Nhập key ở ô ẩn; nếu đã đăng nhập ChatGPT trong Codex, chỉ cần lưu công việc rồi xác nhận áp dụng. Chương trình sao lưu cấu hình, bật kết nối và yêu cầu Codex đóng/mở lại. Không tự mở web trong luồng này.
8
+
9
+ Nếu chưa đăng nhập ChatGPT, Codex được mở để bạn đăng nhập; quay lại terminal và nhấn Enter để tiếp tục. Không nhập mật khẩu ChatGPT tại đây. Có thể hủy bằng Ctrl+C hoặc q; các bước đã lưu được giữ. Một thao tác đóng/mở đã gửi có thể vẫn hoàn tất.
10
+
11
+ Chạy lại thiết lập bằng `codexunlimit setup`. Mở trang quản lý, chat và quota sau bằng `codexunlimit web`. Lệnh `setup` cần terminal tương tác và không nhận key qua tham số hay pipe. Không tự bật khởi động cùng máy; tùy chọn đó vẫn ở khay/cài đặt.
12
+
13
+ ## Thiết lập Codex trên trang web · web.14
14
+
15
+ Trang chủ ưu tiên nhập key, kết nối tài khoản ChatGPT và áp dụng cấu hình Codex. Các bước còn thiếu hoặc cấu hình bị thay đổi được hiển thị ngay trên trang. Hoàn tất key và tài khoản, lưu công việc đang làm rồi chọn **Cấu hình & mở lại Codex**. Có thể bỏ chọn tự mở lại để chỉ lưu cấu hình.
16
+
17
+ Các nút **Mở Codex**, **Mở lại Codex** và **Đóng Codex** điều khiển ứng dụng desktop được nhận diện trên máy; không đóng Codex CLI hoặc extension. Khi Codex chưa chấp nhận đóng, ứng dụng dừng thao tác và hướng dẫn xử lý, không ép tắt. Nếu không tìm thấy ứng dụng hoặc hệ điều hành chưa hỗ trợ, lưu cấu hình rồi mở lại Codex thủ công. **Tắt kết nối Codex Unlimit** khôi phục cấu hình gốc, kể cả khi key đã hết hạn. Chat trên web vẫn ở mục **Trò chuyện**.
18
+
19
+ ## Sửa thông báo kích hoạt · web.13
20
+
21
+ Phân biệt lỗi ngày giờ máy khách, phản hồi xác thực không hợp lệ và key bị máy chủ từ chối. Khi admin đã ghi nhận kích hoạt nhưng máy khách báo lỗi ngày giờ, đồng bộ lại ngày giờ tự động, khởi động lại Codex Unlimit và nhập lại cùng key. Kích hoạt lại trên cùng thiết bị với dữ liệu ứng dụng còn nguyên không tăng số thiết bị hay tính lại thời hạn. Key hết hạn hoặc bị thu hồi thật vẫn bị chặn.
22
+
23
+ ## Cài và thiết lập
24
+
25
+ Mở PowerShell (Windows) hoặc Terminal (macOS/Linux). Cài và bắt đầu thiết lập bằng hai dòng:
26
+
27
+ ```sh
28
+ npm i -g codexunlimit
29
+ codexunlimit
30
+ ```
31
+
32
+ Lần đầu, nhập key ngay trong terminal theo hướng dẫn. Khi được hỏi, lưu công việc rồi nhấn Enter để đồng ý kết nối, lưu cấu hình và mở lại Codex. Nếu máy chưa hỗ trợ mở lại tự động, terminal báo rõ bước cần làm thủ công. Web local là tùy chọn: chạy `codexunlimit web` khi muốn xem trạng thái hoặc chat.
33
+
34
+ Trên Windows, có thể dùng `npm.cmd` và `codexunlimit.cmd` nếu PowerShell chặn file `.ps1`.
35
+
36
+ Trang cài đặt công bố nguồn đang có hiệu lực. Khi gói chính thức trên npmjs đã được xác nhận tại đó, có thể dùng `npm i -g codexunlimit` hoặc thêm `@0.1.0-web.8` để cố định phiên bản. URL sản phẩm vẫn là phương án dự phòng. Không coi tên trần trên registry là chính thức trước thông báo xuất bản. Gói không có hook cài đặt tự chạy. Cần Node/npm có sẵn; không tải/chạy PowerShell từ web hay yêu cầu bộ cài .exe riêng. Kiểm tra môi trường bằng `codexunlimit doctor` (hoặc `--json`); lệnh không đọc token ChatGPT.
37
+
38
+ ## Ảnh và độ ổn định · web.7
39
+
40
+ Công cụ ImageGen gốc của Codex có thể gửi tạo/sửa ảnh qua `/v1/images/generations` và `/v1/images/edits` (cũng hỗ trợ đường dẫn không có `/v1`). Ảnh không đi qua luồng xử lý văn bản/SSE. Tài khoản đã chọn được giữ cho từng request; gateway không tự tạo lại ảnh hoặc đổi tài khoản khi upstream lỗi. Quyền tạo ảnh và quota vẫn do tài khoản upstream quyết định. Đây không phải nút tạo ảnh mới trong chat local và không phải API key OpenAI công khai.
41
+
42
+ Lỗi mạng khi gia hạn lease tạm thời trả 503/429, không tự kết luận key hết hạn. Lease còn hạn vẫn được dùng trong khi thử gia hạn; lease hết hạn không được dùng để vượt kiểm tra. Server từ chối/thu hồi thật vẫn có hiệu lực. Phản hồi HTML từ proxy được đổi thành lỗi JSON ngắn, có hướng dẫn chờ, không tự gửi lại request tốn quota.
43
+
44
+ Trước khi cập nhật, hoàn tất tác vụ, cài lại bằng lệnh npm đang công bố rồi chạy `codexunlimit restart`. Khởi động lại theo cách này giữ cấu hình kết nối, key/pool/hội thoại. Nếu npm báo file đang được dùng, chọn **Dừng để cập nhật** trong ứng dụng rồi cài lại. Với bản từ URL riêng chưa có công bố npmjs, không dùng `npm update -g codexunlimit` theo tên trần.
45
+
46
+ Nếu đã dùng bộ cài quản lý trước đây, không cần cài chồng: dùng `codexunlimit update` của bộ cài đó. Muốn chuyển hẳn sang npm, chuẩn bị Node/npm trước, gỡ bộ cài cũ bằng lệnh `uninstall` của nó (giữ dữ liệu), rồi cài npm. Tránh để hai launcher cùng tên trong PATH khiến mở nhầm phiên bản.
47
+
48
+ ## Tài khoản, quota và Markdown
49
+
50
+ Mở **Tài khoản & quota** trong trang local để xem mức sử dụng 5 giờ/1 tuần, thời điểm reset và lần cập nhật của từng tài khoản. Nút **Cập nhật quota** đọc hạn mức qua công cụ đăng nhập Codex, không gửi tin nhắn. Phản hồi chat/Codex cũng cập nhật số liệu local và bảng quota admin/khách online hiện có. Nếu chưa có dữ liệu hoặc dữ liệu đã cũ, giao diện ghi rõ; không suy thành 0%.
51
+
52
+ Chọn **Thêm tài khoản**, đồng ý sử dụng phiên và mở trang đăng nhập OpenAI. Có thể dùng đăng nhập bằng mã nếu cần. Mỗi tài khoản có phiên riêng, tối đa 10 tài khoản lưu thêm; không thay đổi phiên gốc trong Codex. Sau lần đăng nhập đầu, chọn **Dùng tài khoản này** hoặc chọn ở đầu trang chat. Lựa chọn áp dụng cho yêu cầu mới; yêu cầu đang chạy giữ tài khoản ban đầu. Khi hết quota, ứng dụng báo giới hạn và không tự gửi lại qua tài khoản khác.
53
+
54
+ Phiên trong pool được mã hóa bằng AES-GCM với khóa rút từ khóa thiết bị. Trên Windows, khóa thiết bị được bảo vệ bằng DPAPI của người dùng. Công cụ Codex chỉ dùng file phiên tạm trong thư mục riêng khi đăng nhập/làm mới và file được xóa khi tác vụ kết thúc. Token không được trả cho trang web, không ghi log và không gửi về server quản lý; yêu cầu xử lý vẫn gửi access token cần thiết qua gateway như trước. Tài khoản gốc được liên kết chỉ cung cấp access token tạm thời để đọc quota, không sao chép/luân chuyển refresh token của Codex gốc.
55
+
56
+ Gói npm tự cài thêm `@openai/codex` 0.166.0 cùng binary đúng hệ điều hành và `markdown-it` 15.0.3. Cần kết nối npm registry khi cài; không dùng `--omit=optional` vì sẽ thiếu binary đăng nhập. Dung lượng tải thực tế bao gồm các dependency, không chỉ file `.tgz` của Codex Unlimit.
57
+
58
+ Câu trả lời chat hỗ trợ Markdown khi nhận từng phần và khi mở lại lịch sử: tiêu đề, chữ đậm/nghiêng, danh sách, trích dẫn, bảng, liên kết và khối mã có nút sao chép. HTML thô không được thực thi, liên kết script bị chặn và ảnh bên ngoài không tự tải.
59
+
60
+ ## Khay Windows và tắt ứng dụng
61
+
62
+ Từ web.5, biểu tượng CU xuất hiện trong khay Windows khi ứng dụng chạy; có thể nằm trong dấu mũi tên lên ở thanh tác vụ. Nhấp đúp để mở trang, nhấp phải để chọn **Mở Codex Unlimit**, **Khởi động cùng Windows**, **Khởi động lại**, hoặc **Tắt ứng dụng**. Tự khởi động mặc định tắt; khi bật, chỉ chạy nền trong khay, không mở trình duyệt.
63
+
64
+ **Cài đặt & hỗ trợ → Ứng dụng trên máy** cũng có công tắc khởi động cùng Windows, nút hiện lại biểu tượng khay, hướng dẫn cập nhật và nút tắt. Đóng tab không dừng dịch vụ. **Tắt ứng dụng** hoặc `codexunlimit stop` khôi phục cấu hình Codex do bản này quản lý trước khi dừng, giữ key và dữ liệu. Nếu cấu hình bị bên khác chỉnh sửa, thao tác dừng sẽ báo rõ xung đột thay vì ghi đè.
65
+
66
+ `codexunlimit startup on` / `codexunlimit startup off` bật/tắt shortcut trong Startup của tài khoản Windows hiện tại; không cần quyền quản trị. `codexunlimit restart` khởi động lại. macOS/Linux vẫn dùng lệnh mở và tắt; bản này chưa có khay hay tự khởi động cho hai nền tảng đó.
67
+
68
+ ## Cập nhật và xử lý kết nối
69
+
70
+ Cài gói mới rồi chạy `codexunlimit`: CLI xác minh phiên web đang chạy bằng khóa điều khiển của cùng thư mục dữ liệu, dừng phiên khác phiên bản và mở bản mới. Giữ cấu hình, key và dữ liệu trong lần chuyển này. Không dừng dịch vụ lạ đang chiếm cổng. Với web.1 chưa có giao thức xác minh phiên, chạy `codexunlimit stop` trước. Bản native cũ cần khôi phục kết nối và thoát từ chính bản đó.
71
+
72
+ **Dừng để cập nhật** trong phần mở rộng của cài đặt sẽ giữ cấu hình nhưng dừng dịch vụ; Codex tạm thời mất kết nối cho tới khi mở bản mới. Gỡ ứng dụng: tắt **Khởi động cùng Windows**, chọn **Tắt ứng dụng**, rồi chạy `npm uninstall -g codexunlimit`. Dữ liệu riêng vẫn được giữ.
73
+
74
+ Khi không kết nối được, chọn **Kiểm tra kết nối**. Trang phân biệt key, tài khoản và cấu hình; profile riêng, cấu hình native, quyền ghi và bản sao khôi phục hỏng có hướng xử lý cụ thể. Nếu Codex đã reset cấu hình về trực tiếp, **Kết nối lại với Codex** tạo lại kết nối và sao lưu cấu hình hiện tại. Không tự sửa cấu hình đang được ứng dụng khác quản lý.
75
+
76
+ ## Dữ liệu và tương thích
77
+
78
+ - Nền tảng riêng: `https://codexunlimit.tudonghoa.me`, cổng local mặc định `18457`. Không dùng dịch vụ hay thư mục TDH Relay/toolcodex.
79
+ - Dữ liệu web ở `%LOCALAPPDATA%/CodexunlimitWeb` trên Windows, `~/Library/Application Support/CodexunlimitWeb` trên macOS. Khóa thiết bị dùng DPAPI trên Windows; file riêng tư theo quyền người dùng trên macOS/Linux.
80
+ - Lịch sử chat ở trình duyệt này, tối đa 30 cuộc / 100 tin mỗi cuộc. Khi gửi tin, nội dung và mã truy cập đi qua dịch vụ Codex Unlimit; server không lưu nội dung chat hoặc mã truy cập. Refresh token do Codex quản lý.
81
+ - Gói web dùng kích hoạt thiết bị riêng. Nếu chuyển từ native, khôi phục kết nối và dừng bản native trước; quản trị viên có thể giải phóng slot cũ. Không tự chuyển khóa Keychain/DPAPI từ ứng dụng native.
82
+ - Chat sử dụng phiên ChatGPT từ Codex; khi hết phiên, đăng nhập lại trong Codex. Chưa có đăng nhập ChatGPT độc lập trong web.
83
+ - Không yêu cầu Authenticode hoặc Apple Developer ID để chạy gói Node; xác minh license và chữ ký request vẫn thực hiện. Policy luôn ở server.
84
+
85
+ ## Kiểm tra / đóng gói từ repository
86
+
87
+ ```sh
88
+ npm run start:local
89
+ npm run pack:local
90
+ npm test
91
+ ```
92
+
93
+ `pack:local` chỉ đóng gói thư mục connector theo allowlist; không lấy mã server/engine hoặc dữ liệu vận hành. Package đang private để tránh phát hành nhầm lên npm. Sau khi được phân phối, khách có thể cài trực tiếp từ URL gói tải; không cần public server source.
76
94
 
77
95
  ### Kiểm tra phiên bản và cập nhật
78
96
 
package/cli.mjs CHANGED
@@ -1,110 +1,2 @@
1
- #!/usr/bin/env node
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import os from 'node:os';
5
- import crypto from 'node:crypto';
6
- import {spawn} from 'node:child_process';
7
- import {fileURLToPath} from 'node:url';
8
- import {ClientState} from './state.mjs';
9
- import {LicenseClient} from './license-client.mjs';
10
- import {createConnector} from './relay.mjs';
11
- import {createLocalApp,createBrowserTicket,readCodexAccount} from './app.mjs';
12
- import {AccountPool} from './account-pool.mjs';
13
- import {DEFAULT_PORT,VERSION} from './product.mjs';
14
- import {unsyncCodex,migrateCodexTransport} from './config.mjs';
15
- import {createWindowsIntegration} from './windows.mjs';
16
- import {errors} from './web/messages.js';
17
- import {diagnoseRuntime} from './doctor.mjs';
18
-
19
- const args=process.argv.slice(2),command=args[0]&&!args[0].startsWith('--')?args[0]:'open';
20
- function option(name,fallback){const i=args.indexOf(name);if(i<0)return fallback;if(!args[i+1]||args[i+1].startsWith('--'))throw Error('Thiếu giá trị cho '+name);return args[i+1];}
21
- const home=path.resolve(option('--home',process.platform==='win32'?path.join(process.env.LOCALAPPDATA??path.join(os.homedir(),'AppData/Local'),'CodexunlimitWeb'):process.platform==='darwin'?path.join(os.homedir(),'Library/Application Support/CodexunlimitWeb'):path.join(os.homedir(),'.local/share/codexunlimit')));
22
- async function main(){
23
- if(args.includes('--version')){console.log(VERSION);return;}
24
- if(command==='doctor'){
25
- const result=await diagnoseRuntime();if(args.includes('--json'))console.log(JSON.stringify(result));else{for(const check of result.checks)console.log((check.ok?'OK ':'THIẾU ')+check.name+(check.version?' '+check.version:''));console.log(result.ok?'Môi trường đã sẵn sàng. Key/tài khoản được kiểm tra trong ứng dụng.':'Cài lại gói npm, giữ dependency tùy chọn. Không cần xóa key, đăng xuất ChatGPT hoặc reset thiết bị.');}if(!result.ok)process.exitCode=1;return;
26
- }
27
- if(args.includes('--help')){console.log('Codex Unlimit\n codexunlimit diagnostics Xuất log kết nối đã loại thông tin riêng tư\n codexunlimit doctor Kiểm tra Node, SQLite và Codex runtime\n codexunlimit Mở ứng dụng và khay Windows\n codexunlimit stop Khôi phục kết nối Codex và tắt ứng dụng\n codexunlimit restart Khởi động lại bằng bản đã cài mới nhất\n codexunlimit startup on Tự mở trong khay khi đăng nhập Windows\n codexunlimit startup off Tắt khởi động cùng Windows\n codexunlimit --no-open Chạy nền, không mở trình duyệt\n --no-tray Không mở khay (dành cho kiểm thử)\n --home <thư mục> Dữ liệu riêng\n --port <số cổng> Cổng cho lần cài đầu');return;}
28
- if(!['open','serve','stop','restart','startup','diagnostics'].includes(command))throw Error('Lệnh chưa đúng. Xem codexunlimit --help');
29
- const state=new ClientState(home);let serving=false;
30
- try{
31
- if(command==='diagnostics'){console.log(JSON.stringify({version:VERSION,requests:state.diagnostics()},null,2));return;}
32
- const port=Number(option('--port',DEFAULT_PORT));if(!Number.isInteger(port)||port<1024||port>65535)throw Error('Cổng cần nằm trong khoảng 1024–65535.');
33
- state.initialize({port,codexHome:state.get('codex_home')??process.env.CODEX_HOME??path.join(os.homedir(),'.codex')});
34
- const desktop=createWindowsIntegration(state);
35
- if(command==='startup'){
36
- if(!['on','off'].includes(args[1]))throw Error('Dùng codexunlimit startup on hoặc codexunlimit startup off.');
37
- await desktop.setStartup(args[1]==='on');console.log(args[1]==='on'?'Đã bật khởi động cùng Windows.':'Đã tắt khởi động cùng Windows.');return;
38
- }
39
- const base='http://127.0.0.1:'+state.settings.get('relay.port');
40
- async function health(){try{const r=await fetch(base+'/healthz',{signal:AbortSignal.timeout(1000)});return await r.json();}catch{return null;}}
41
- async function ownInstance(){
42
- const nonce=crypto.randomBytes(24).toString('hex'),expected=crypto.createHmac('sha256',state.get('control_token')).update('cu-instance:'+nonce).digest('hex');
43
- const response=await fetch(base+'/control/identity',{headers:{'x-cu-nonce':nonce},signal:AbortSignal.timeout(1500)});
44
- const value=await response.json();if(value.proof!==expected)throw Error('Cổng đang chạy một phiên khác. Dừng bản cũ trước hoặc chọn cổng khác.');
45
- }
46
- if(command==='serve'){
47
- const license=new LicenseClient(state);
48
- const pool=new AccountPool(state,{privateKey:license.device.privateKey,defaultAccount:()=>readCodexAccount(state)});
49
- const server=createConnector(state,{license,pool,localApp:createLocalApp(state,{license,desktop,pool,onStop:shutdown}),prepareStop:mode=>{if(mode==='exit')unsyncCodex(state);},onStop:shutdown});
50
- let stopping=false;
51
- function shutdown(){if(stopping)return;stopping=true;server.close(async()=>{try{await pool.close();}finally{state.close();process.exit(0);}});server.closeIdleConnections();setTimeout(()=>server.closeAllConnections(),1500).unref();}
52
- server.listen(state.settings.get('relay.port'),'127.0.0.1');await new Promise((resolve,reject)=>{server.once('listening',resolve);server.once('error',reject);});
53
- try{migrateCodexTransport(state);}catch(error){console.error(errors[error.message]??errors.operation_failed);}
54
- serving=true;process.once('SIGINT',shutdown);process.once('SIGTERM',shutdown);
55
- if(desktop.status().startup)desktop.setStartup(true).catch(()=>console.error(errors.startup_update_failed));
56
- if(process.platform==='win32'&&!args.includes('--no-tray'))desktop.showTray().catch(()=>console.error(errors.tray_unavailable));
57
- return;
58
- }
59
- let existing=await health();
60
- if(existing&&(existing.service!=='codexunlimit-connector'||existing.mode!=='desktop'))throw Error('Cổng đang được ứng dụng khác sử dụng. Hãy chọn --port khác với --home riêng.');
61
- // Compatibility for customers who installed the update before stopping web.1.
62
- if(existing&&!(command==='stop'&&existing.version==='0.1.0-web.1'))await ownInstance();
63
- async function stopInstance(mode){
64
- if(mode==='exit')unsyncCodex(state);
65
- if(!existing)return;
66
- const response=await fetch(base+'/control/stop',{method:'POST',headers:{'x-cu-control':state.get('control_token'),'x-cu-stop-mode':mode},signal:AbortSignal.timeout(5000)});
67
- if(!response.ok)throw Error('Chưa tắt được phiên đang chạy. Mở Cài đặt & hỗ trợ để kiểm tra kết nối.');
68
- for(let attempt=0;attempt<50;attempt++){
69
- if(!await health()){existing=null;return;}
70
- await new Promise(resolve=>setTimeout(resolve,100));
71
- }
72
- throw Error('Phiên cũ chưa tắt hoàn toàn. Chờ một lát rồi mở lại Codex Unlimit.');
73
- }
74
- if(command==='stop'){await stopInstance('exit');console.log('Codex Unlimit đã tắt. Kết nối Codex đã được khôi phục.');return;}
75
- if(existing&&(command==='restart'||existing.version!==VERSION)){
76
- console.log('Đang khởi động lại Codex Unlimit '+VERSION+'…');await stopInstance('update');
77
- }
78
- if(existing){const page=await fetch(base+'/app/',{signal:AbortSignal.timeout(2000)});if(!page.ok)throw Error('Bản cũ đang chạy. Dừng Codex Unlimit cũ trước khi mở phiên bản web.');}
79
- else{
80
- const logs=path.join(home,'logs');fs.mkdirSync(logs,{recursive:true,mode:0o700});
81
- const output=fs.openSync(path.join(logs,'web.log'),'a',0o600);
82
- const child=spawn(process.execPath,[fileURLToPath(import.meta.url),'serve','--home',home,...(args.includes('--no-tray')?['--no-tray']:[])],{detached:true,windowsHide:true,stdio:['ignore',output,output]});
83
- let launchError=false;child.once('error',()=>{launchError=true;});
84
- child.unref();fs.closeSync(output);
85
- for(let i=0;i<100;i++){existing=await health();if(existing||launchError)break;await new Promise(r=>setTimeout(r,150));}
86
- if(!existing)throw Error('Chưa mở được dịch vụ. Xem '+path.join(logs,'web.log'));
87
- await ownInstance();
88
- }
89
- const url=base+'/app/#ticket='+createBrowserTicket(state);
90
- console.log('Codex Unlimit đang chạy trên máy của bạn.\n'+url+'\nGiữ đường dẫn này riêng tư; liên kết mở trang có hiệu lực 2 phút.');
91
- if(!args.includes('--no-open')){
92
- // Keep the complete local URL (including its ticket) out of shell source.
93
- const windows=process.platform==='win32';
94
- const exe=windows?path.join(process.env.SystemRoot??'C:/Windows','System32/WindowsPowerShell/v1.0/powershell.exe'):process.platform==='darwin'?'open':'xdg-open';
95
- const launchArgs=windows?['-NoProfile','-NonInteractive','-WindowStyle','Hidden','-Command',"$ErrorActionPreference='Stop'; Start-Process -FilePath $env:CU_BROWSER_URL"]:[url];
96
- try{
97
- await new Promise((resolve,reject)=>{
98
- const child=spawn(exe,launchArgs,{windowsHide:true,stdio:'ignore',env:{...process.env,CU_BROWSER_URL:url}});
99
- const timeout=setTimeout(()=>{child.kill();reject(Error('browser_open_timeout'));},15000);
100
- child.once('error',error=>{clearTimeout(timeout);reject(error);});
101
- child.once('exit',code=>{clearTimeout(timeout);code===0?resolve():reject(Error('browser_open_failed'));});
102
- });
103
- }catch{
104
- console.error('Không tự mở được trình duyệt. Sao chép đường dẫn local ở trên vào trình duyệt.');
105
- process.exitCode=1;
106
- }
107
- }
108
- }finally{if(!serving)state.close();}
109
- }
110
- main().catch(error=>{console.error(errors[error.message]??error.message);process.exitCode=1;});
1
+ #!/usr/bin/env node
2
+ const _0xca5444=_0x3bfd;(function(_0x25203b,_0x8b31fc){const _0x3ec97d=_0x3bfd,_0x263655=_0x25203b();while(!![]){try{const _0x43fdd6=-parseInt(_0x3ec97d(0x1c3))/0x1*(-parseInt(_0x3ec97d(0x1b2))/0x2)+parseInt(_0x3ec97d(0x1eb))/0x3*(-parseInt(_0x3ec97d(0x1db))/0x4)+-parseInt(_0x3ec97d(0x1b0))/0x5*(-parseInt(_0x3ec97d(0x180))/0x6)+parseInt(_0x3ec97d(0x1a6))/0x7+-parseInt(_0x3ec97d(0x17f))/0x8*(parseInt(_0x3ec97d(0x1ea))/0x9)+-parseInt(_0x3ec97d(0x1b6))/0xa+parseInt(_0x3ec97d(0x1ca))/0xb;if(_0x43fdd6===_0x8b31fc)break;else _0x263655['push'](_0x263655['shift']());}catch(_0x276448){_0x263655['push'](_0x263655['shift']());}}}(_0x2ad0,0xadd5b));import{B as _0x383952,C as _0x553e27,D as _0x437e89,E as _0x449817,H as _0x4459b4,I as _0x8af093,J as _0xa672a8,Q as _0x52c06e,R as _0x5c1ed9,S as _0xea9bd1,T as _0x3bc0f4,U as _0x3f825a,V as _0x3d541f,W as _0x23347e,X,Y as _0x1a1495,a as _0x27bda6,c as _0x18fb7d,f as _0x113fcb,s as _0x577da8,x as _0x185b2e}from'./shared-CUSGWETD.mjs';import _0xabaf0 from'node:fs';function _0x3bfd(_0x36be93,_0xa08536){_0x36be93=_0x36be93-0x175;const _0x2ad0ac=_0x2ad0();let _0x3bfdb8=_0x2ad0ac[_0x36be93];if(_0x3bfd['arFtUG']===undefined){var _0x292c16=function(_0x337584){const _0x519335='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x31de8b='',_0x383952='';for(let _0x553e27=0x0,_0x437e89,_0x449817,_0x4459b4=0x0;_0x449817=_0x337584['charAt'](_0x4459b4++);~_0x449817&&(_0x437e89=_0x553e27%0x4?_0x437e89*0x40+_0x449817:_0x449817,_0x553e27++%0x4)?_0x31de8b+=String['fromCharCode'](0xff&_0x437e89>>(-0x2*_0x553e27&0x6)):0x0){_0x449817=_0x519335['indexOf'](_0x449817);}for(let _0x8af093=0x0,_0xa672a8=_0x31de8b['length'];_0x8af093<_0xa672a8;_0x8af093++){_0x383952+='%'+('00'+_0x31de8b['charCodeAt'](_0x8af093)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x383952);};_0x3bfd['tZcRze']=_0x292c16,_0x3bfd['UdgBoP']={},_0x3bfd['arFtUG']=!![];}const _0x1c0f36=_0x2ad0ac[0x0];_0x3bfd['RoZBgV']!==_0x1c0f36&&(_0x3bfd['UdgBoP']={},_0x3bfd['RoZBgV']=_0x1c0f36);const _0x25d9ad=_0x3bfd['UdgBoP'][_0x36be93];return _0x25d9ad===undefined?(_0x3bfdb8=_0x3bfd['tZcRze'](_0x3bfdb8),_0x3bfd['UdgBoP'][_0x36be93]=_0x3bfdb8):_0x3bfdb8=_0x25d9ad,_0x3bfdb8;}import _0x2c6e49 from'node:path';import _0x19f6d2 from'node:os';import _0x4edbff from'node:crypto';import{spawn as _0x5198b6}from'node:child_process';function _0x2ad0(){const _0x5b50a0=['lMnVzgv4','BM93','ls1OzwXW','CMvZDgfYDa','zgv2AwnL','ls1UBY10CMf5','Aw5KzxHpzG','y2XVC2u','q29KzxH1BMXPBwL0v2vI','C2vYDMu','BwvZC2fNzq','q2JgSgeGBEg7NYdeKCAW4BUJyYbK4BUly2GGDUg7Ps4GwgvTia','y3jLyxrLsg1HyW','otK4ndq1DuTkuerj','y3uTAw5ZDgfUy2u6','B25Jzq','Dw5Yzwy','y29KzxHFAg9Tzq','zgLHz25VC3rPy3m','qxbWrgf0ys9mB2nHBa','ls12zxjZAw9U','y29UzMLNDxjLza','BgLZDgvUAw5N','mZvuALviB1y','C2v0','odG0mZK0Aw1ODNrr','u0Lhvevstq','l2fWCc8JDgLJA2v0pq','q+g7Lw5Nimsryw5NimsrXRdHU6nJiog7Qw5NigtHU6vUzYbRAmoHyYbZ4BUTigtHU6vUzY4GsmoJEsbJAog7Jw4Gls1WB3j0igTOW6fJihBHU5TPic0TAg9TzsbYACoQBMCU','nti1mJG5me1hueHIva','y2f0y2G','A2LSBa','y29KzxH1BMXPBwL0lwnVBM5Ly3rVCG','D2vIlMXVzW','Ag9TzwrPCG','Agv4','EgrNlw9Wzw4','ChjPDMf0zuTLEq','DMvYC2LVBG','Dg9tDhjPBMC','ls1OB21L','q2JgSgeGDog6R3qGXjhgSog7O2mGCgHPW6PUimsryw5NignO4BQHEs4GtEg7NYbdW6bPimsr4BQ3DcaMigJHU5CGDhlHU6mGXjhHU4mGA2NHU4nTihrYysbR4BQ/DcbU4BUrAs4','mMXqtwryqq','C3rHCNrZv2L0Aa','veHj4BQ+vsa','DxbKyxrL','C3rHCNr1Ca','yNjVD3nLCL9VCgvUx3rPBwvVDxq','DgvYBwLUywXFC2v0DxbFDhr5x3jLCxvPCMvK','mteXmJe4ntHKzgzhDNa','Bg9NCW','ue9tva','C3rVCa','B3bLCMf0Aw9Ux2zHAwXLza','DhjHEv91BMf2ywLSywjSzq','q8oGAsbS4BQHAsbNW7nPig5WBsWGz2NHU68GzgvWzw5Kzw5JEsb0W7L5ignO4BUnBI4Gs2JdTg5NigpHUQDUihJdS2eGA2v5lcdeKCsdBMCGEhxHUQv0ienOyxrhufqGAg/HURDJihjLC2v0ihrOAEg6V3qGyUg7IY4','Ahr0CdOVlZeYnY4WlJaUmtO','CMfUzg9TqNL0zxm','CMvSyxKUCg9YDa','zg9JDg9Y','u3LZDgvTmZiVv2LUzg93C1bVD2vYu2HLBgWVDJeUmc9WB3DLCNnOzwXSlMv4zq','ANnVBG','D2LUmZi','C2LNBMfS','mti3lJaUmc4X','lvDPBMrVD1n0EwXL','nhbntLrqyW','jevYCM9Yqwn0Aw9UuhjLzMvYzw5Jzt0Nu3rVCcC7ifn0yxj0lvbYB2nLC3mGluzPBgvqyxrOicrLBNy6q1vFqLjpv1nfuL9vuKW','yNjVD3nLCL9VCgvUx2zHAwXLza','zxHLy1bHDgG','C2v0DgLUz3m','Aw5PDgLHBgL6zq','zxjYB3i','l2HLywX0AhO','tCo0Asb0CSAW4BUDBMCGXjhdOYbZ4BQ1BIbZW6bUzY4Gs2v5l3tdOgKGA2HV4BQJBIdeKCAW4BUJyYbRAEg7G20GDhjHihrYB25Niog7Qw5NigtHU6vUzY4','y29UDhjVBf90B2TLBG','zgLNzxn0','t0SGica','sgLKzgvU','AxnuvfK','rmo5BMCGy29KzxH1BMXPBwL0ihn0yxj0DxaGB24GAg/HURDJignVzgv4Dw5SAw1PDcbZDgfYDhvWig9MzI4','mtHPCKzzDue','mJK3mti4ne9OsNzLBa','yxjNDG','lu5VBKLUDgvYywn0AxzL','BwTKAxjtEw5J','z2v0','zxHPDenVzgu','Aw5JBhvKzxm','C2vYDMLJzq','C2v0u3rHCNr1Ca','cKDP4BUVimsrXRdHU51UzYbK4BQRBIbUW6b5ihjPW6PUzYb0XRa7igXPW6PUigVHUR90ig3HU58GDhjHBMCGy8oZigHP4BUhDsbS4BUXyYaYihbOW7P0lG','y2XVC2vjzgXLq29UBMvJDgLVBNm','s2JdTg5NihtHU7eGBEg7NYdeKCAW4BUJyYb0CSoSBMGGzhv54BUhDc4Gu2fVignOW6LWimsrXRdHU51UzYbK4BQRBIbSB2nHBcdHU58GDhldQM4GDSoGBYb0CSoSBMGGzhv54BUhDc4','ls1WB3j0','l2fWCc8','B3bLBLn5BMm','XjddOYb04BQVDcbRAog7N2KGXjhHU5LUzYbJW7LUzYbxAw5KB3DZlG','DgvYBwLUywXF','C3rYAw5NAwz5','C2v0Dxa','AxnjBNrLz2vY','DgLTzw91Da','Bg9N','y2XVC2vbBgXdB25Uzwn0Aw9UCW','XjbHBMCGA2JHU59Pimsr4BUzBMCGBog6OwKGq29KzxGGvw5SAw1PDca','mZaZotG0ofn4u1j4Aq','odeZotqYzuPqtxPp','C3rKAw4','C2HHmJu2','zxHPDa','tgLICMfYEs9bChbSAwnHDgLVBIbtDxbWB3j0l0nVzgv4Dw5SAw1PDfDLyG','ls1UBY1VCgvU','ugHPW6PUigpfQsbJAmAWysb04BQVDcbOB8oGBIb0B8oGBI4Gq2JHU50GBEg7MxqGBmoHDcbY4BUtAsbT4BUFigZHUQfPienVzgv4ifvUBgLTAxqU','CgXHDgzVCM0','XjddOYbI4BQTDcbRAog7N2KGXjhHU5LUzYbJW7LUzYbxAw5KB3DZlG','ChjVB2y','y29Kzq','y29KzxGUCMvZDgfYDef0','AM9PBG','zgfYD2LU','u0Lhsu5u','q29KzxGGvw5SAw1PDaOGignVzgv4Dw5SAw1PDcbKAwfNBM9ZDgLJCYaGicbyDEg6PxqGBg9NigVHUR90ig7HU5fPimsrW6mGBg/HUQfPihrOW7rUzYb0Aw4GCMNdQM5NihtgSaOGignVzgv4Dw5SAw1PDcbKB2n0B3iGicaGicaGicblAEg7G20GDhjHie5VzguSifnrtgL0zsb2W6aGq29KzxGGCNvUDgLTzqOGignVzgv4Dw5SAw1PDcaGicaGicaGicaGicaGicbuAgNHUR90igZHUQ1Wig5Oyw5Oiog7NYbS4BQNBIdeKEg6P3u7ig3HU58GDhjHBMCGCxxHUQnUigZdVsbRAgKGXjhdOYb4B25NcIaGy29KzxH1BMXPBwL0ihnLDhvWicaGicaGicaGie5O4BQTCcbRzxKG4BQPBIWGy+g6PxuGAmoSBMGGDSoGig3HU58GBog6OwKGq29KzxGGDhjVBMCGDgvYBwLUywWkicbJB2rLEhvUBgLTAxqGD2vIicaGicaGicaGicaGtEg7NYdHU6LUzYbK4BULBMCGD2vIihBdOcbRAgf5ifDPBMrVD3mkicbJB2rLEhvUBgLTAxqGC3rVCcaGicaGicaGicaGs2JdTgKGCgJHU6vJigVHUR90ig7HU5fPienVzgv4ihBdOcb04BQVDcdHU6LUzYbK4BULBMCkicbJB2rLEhvUBgLTAxqGCMvZDgfYDcaGicaGicaGs2JHU59Pimsr4BUzBMCGBog6OwKGyUg6Sw5NiglHUQnUimsrW6mGy8oGAsbT4BUBAsbUAog6PxqkicbJB2rLEhvUBgLTAxqGC3rHCNr1CcbVBIaGicaGvog7SsbT4BUFihrYB25NigTOyxKGA2HPimsrXinUzYbUAog6RxaGv2LUzg93CWOGignVzgv4Dw5SAw1PDcbZDgfYDhvWig9MzIaGicbu4BQVDcbRAog7N2KGXjhHU5LUzYbJW7LUzYbxAw5KB3DZcIaGy29KzxH1BMXPBwL0ic0TBM8TB3bLBIaGicaGienO4BQHEsbU4BUbBIWGA2JdTg5Nig3HU58GDhldRg5Oigr1EEg7H3qkicaTlw5VlxrYyxKGicaGicaGicaGicaGicaGicaGs2JdTg5Nig3HU58GA2HHEsaOzmoGBMGGy2HVigTP4BUdBsb0Aog7RsKkicaTlwHVBwuGphrOXRaGBEg7Pwm+icaGicaGicaGicaGrog7RYbSAEg7H3uGCMNdQM5NcIaGls1WB3j0idXZ4BUrigpHU5vUzZ4GicaGicaGicaGicbd4BUvBMCGy2HVigZHUQDUigpdOgKGXjhHUQD1','l2nVBNrYB2WVC3rVCa','AwDUB3jL','y2HLy2TZ','qUg6O24Gy8wPimsryw5NignO4BQHEs4Grog7Q25NienVzgv4ifvUBgLTAxqGy8wPihrYXRdHU5TJigTOAsbT4BUFihbOACoQBIbI4BQJBIb3zwiU','DgvYBwLUywXFC2v0DxbFy2fUy2vSBgvK','zgvZA3rVCa','B3bLBG','mc4XlJaTD2vIlJe','C3rKB3v0'];_0x2ad0=function(){return _0x5b50a0;};return _0x2ad0();}import{fileURLToPath as _0x126536}from'node:url';var n=process[_0xca5444(0x1ec)]['slice'](0x2),E=n[0x0]&&!n[0x0][_0xca5444(0x1c4)]('--')?n[0x0]:null,l=E==='web'?_0xca5444(0x196):E??'open',J=!!(process[_0xca5444(0x181)][_0xca5444(0x1e8)]&&process[_0xca5444(0x198)]['isTTY']);function Q(_0x2dd796,_0x9eee42){const _0x6e19ce=_0xca5444;let _0x4b2d75=n[_0x6e19ce(0x19f)](_0x2dd796);if(_0x4b2d75<0x0)return _0x9eee42;if(!n[_0x4b2d75+0x1]||n[_0x4b2d75+0x1][_0x6e19ce(0x1c4)]('--'))throw Error('Thi\u1EBFu\x20gi\xE1\x20tr\u1ECB\x20cho\x20'+_0x2dd796);return n[_0x4b2d75+0x1];}var _=_0x2c6e49['resolve'](Q('--home',process[_0xca5444(0x187)]===_0xca5444(0x1d7)?_0x2c6e49['join'](process.env.LOCALAPPDATA??_0x2c6e49['join'](_0x19f6d2[_0xca5444(0x1bb)](),_0xca5444(0x1ac)),_0xca5444(0x1a1)):process[_0xca5444(0x187)]==='darwin'?_0x2c6e49[_0xca5444(0x18c)](_0x19f6d2[_0xca5444(0x1bb)](),_0xca5444(0x184)):_0x2c6e49['join'](_0x19f6d2['homedir'](),'.local/share/codexunlimit')));async function V(){const _0x5e4fff=_0xca5444;if(n[_0x5e4fff(0x1f1)](_0x5e4fff(0x1ad))){console[_0x5e4fff(0x17c)](_0x27bda6);return;}if(l===_0x5e4fff(0x1d4)){let _0x38d05c=await _0x3bc0f4();if(n[_0x5e4fff(0x1f1)]('--json'))console['log'](JSON[_0x5e4fff(0x178)](_0x38d05c));else{for(let _0x3da702 of _0x38d05c[_0x5e4fff(0x192)])console[_0x5e4fff(0x17c)]((_0x3da702['ok']?_0x5e4fff(0x1e6):_0x5e4fff(0x1c5))+_0x3da702['name']+(_0x3da702[_0x5e4fff(0x1bf)]?'\x20'+_0x3da702[_0x5e4fff(0x1bf)]:''));console[_0x5e4fff(0x17c)](_0x38d05c['ok']?_0x5e4fff(0x1e3):_0x5e4fff(0x1d0));}_0x38d05c['ok']||(process['exitCode']=0x1);return;}if(n[_0x5e4fff(0x1f1)](_0x5e4fff(0x19b))){console['log'](_0x5e4fff(0x18f));return;}if(n['some'](_0x4630ed=>/^--(?:key|activation-key)(?:=|$)/['test'](_0x4630ed)))throw Error('Kh\xF4ng\x20truy\u1EC1n\x20key\x20v\xE0o\x20d\xF2ng\x20l\u1EC7nh.\x20Ch\u1EA1y\x20codexunlimit\x20setup\x20\u0111\u1EC3\x20nh\u1EADp\x20key\x20\u1EA9n.');if(l==='setup'&&!J)throw Object['assign'](Error(_0x5e4fff(0x1c9)),{'code':'terminal_setup_tty_required'});if(![_0x5e4fff(0x196),_0x5e4fff(0x179),_0x5e4fff(0x1a2),_0x5e4fff(0x1cd),'restart',_0x5e4fff(0x1c7),_0x5e4fff(0x1ab)][_0x5e4fff(0x1f1)](l))throw Error('L\u1EC7nh\x20ch\u01B0a\x20\u0111\xFAng.\x20Xem\x20codexunlimit\x20--help');let _0x43333d=new _0x113fcb(_),_0x4d1cd2=!0x1;try{if(l===_0x5e4fff(0x1ab)){console[_0x5e4fff(0x17c)](JSON['stringify']({'version':_0x27bda6,'requests':_0x43333d[_0x5e4fff(0x1ab)]()},null,0x2));return;}let _0x18c704=Number(Q(_0x5e4fff(0x1f7),0x4819));if(!Number[_0x5e4fff(0x17a)](_0x18c704)||_0x18c704<0x400||_0x18c704>0xffff)throw Error('C\u1ED5ng\x20c\u1EA7n\x20n\u1EB1m\x20trong\x20kho\u1EA3ng\x201024\u201365535.');_0x43333d[_0x5e4fff(0x1e0)]({'port':_0x18c704,'codexHome':_0x43333d['get'](_0x5e4fff(0x1aa))??process.env.CODEX_HOME??_0x2c6e49[_0x5e4fff(0x18c)](_0x19f6d2[_0x5e4fff(0x1bb)](),_0x5e4fff(0x199))});let _0x243133=_0x3d541f({'command':l,'explicitCommand':!!E,'interactive':J,'noOpen':n[_0x5e4fff(0x1f1)](_0x5e4fff(0x185)),'activated':!!_0x43333d[_0x5e4fff(0x1ef)]('license.activation'),'connected':!!_0x43333d[_0x5e4fff(0x1ef)]('web.connected'),'configured':_0x553e27(_0x43333d)[_0x5e4fff(0x1ae)],'restartRequired':!!_0x43333d[_0x5e4fff(0x1ef)](_0x5e4fff(0x18b))}),_0x52bb13=_0x5c1ed9(_0x43333d);if(l===_0x5e4fff(0x1c7)){if(!['on','off']['includes'](n[0x1]))throw Error(_0x5e4fff(0x1e9));await _0x52bb13[_0x5e4fff(0x1f3)](n[0x1]==='on'),console[_0x5e4fff(0x17c)](n[0x1]==='on'?_0x5e4fff(0x188):_0x5e4fff(0x176));return;}let _0x1ceba5=_0x5e4fff(0x1d1)+_0x43333d[_0x5e4fff(0x1df)][_0x5e4fff(0x1ef)](_0x5e4fff(0x1d3));async function _0x413b9b(){const _0x33c6f1=_0x5e4fff;try{return await(await fetch(_0x1ceba5+_0x33c6f1(0x1e2),{'signal':AbortSignal[_0x33c6f1(0x17b)](0x3e8)}))[_0x33c6f1(0x1d6)]();}catch{return null;}}async function _0x3c0770(){const _0x2fef24=_0x5e4fff;let _0x54fd0b=_0x4edbff[_0x2fef24(0x1d2)](0x18)[_0x2fef24(0x1c0)](_0x2fef24(0x1bc)),_0x365915=_0x4edbff[_0x2fef24(0x1a5)](_0x2fef24(0x182),_0x43333d[_0x2fef24(0x1ef)](_0x2fef24(0x1e4)))[_0x2fef24(0x1c6)](_0x2fef24(0x1a7)+_0x54fd0b)[_0x2fef24(0x1e5)](_0x2fef24(0x1bc));if((await(await fetch(_0x1ceba5+'/control/identity',{'headers':{'x-cu-nonce':_0x54fd0b},'signal':AbortSignal['timeout'](0x5dc)}))[_0x2fef24(0x1d6)]())[_0x2fef24(0x189)]!==_0x365915)throw Error('C\u1ED5ng\x20\u0111ang\x20ch\u1EA1y\x20m\u1ED9t\x20phi\xEAn\x20kh\xE1c.\x20D\u1EEBng\x20b\u1EA3n\x20c\u0169\x20tr\u01B0\u1EDBc\x20ho\u1EB7c\x20ch\u1ECDn\x20c\u1ED5ng\x20kh\xE1c.');}if(l===_0x5e4fff(0x1a2)){let _0x5df66b=function(){const _0x52cef9=_0x5e4fff;_0x354795||(_0x354795=!0x0,_0x181499['close'](async()=>{const _0x26828d=_0x3bfd;try{await _0x5dbd20['close']();}finally{_0x43333d[_0x26828d(0x1a0)](),process['exit'](0x0);}}),_0x181499[_0x52cef9(0x1f5)](),setTimeout(()=>_0x181499[_0x52cef9(0x17d)](),0x5dc)['unref']());},_0x1f2d33=new _0x577da8(_0x43333d),_0x5dbd20=new _0x52c06e(_0x43333d,{'privateKey':_0x1f2d33[_0x5e4fff(0x19d)][_0x5e4fff(0x1be)],'defaultAccount':()=>_0x8af093(_0x43333d)}),_0x181499=_0x185b2e(_0x43333d,{'license':_0x1f2d33,'pool':_0x5dbd20,'localApp':_0xa672a8(_0x43333d,{'license':_0x1f2d33,'desktop':_0x52bb13,'pool':_0x5dbd20,'codexDesktop':_0xea9bd1(),'onStop':_0x5df66b}),'prepareStop':_0x19284e=>{_0x19284e==='exit'&&_0x383952(_0x43333d);},'onStop':_0x5df66b}),_0x354795=!0x1;_0x181499['listen'](_0x43333d['settings'][_0x5e4fff(0x1ef)](_0x5e4fff(0x1d3)),_0x5e4fff(0x1d9)),await new Promise((_0xb494fc,_0x4c3cf0)=>{const _0x40b3d4=_0x5e4fff;_0x181499[_0x40b3d4(0x1a8)](_0x40b3d4(0x1af),_0xb494fc),_0x181499[_0x40b3d4(0x1a8)](_0x40b3d4(0x1e1),_0x4c3cf0);});try{_0x437e89(_0x43333d);}catch(_0x440953){console[_0x5e4fff(0x1e1)](_0x449817[_0x440953[_0x5e4fff(0x1a3)]]??_0x449817[_0x5e4fff(0x1ce)]);}_0x4d1cd2=!0x0,process['once'](_0x5e4fff(0x18e),_0x5df66b),process[_0x5e4fff(0x1a8)](_0x5e4fff(0x1b3),_0x5df66b),_0x52bb13['status']()[_0x5e4fff(0x1c7)]&&_0x52bb13['setStartup'](!0x0)['catch'](()=>console[_0x5e4fff(0x1e1)](_0x449817['startup_update_failed'])),process[_0x5e4fff(0x187)]===_0x5e4fff(0x1d7)&&!n['includes'](_0x5e4fff(0x19e))&&_0x52bb13['showTray']()[_0x5e4fff(0x1b7)](()=>console['error'](_0x449817[_0x5e4fff(0x1cf)]));return;}let _0x55f5f8=await _0x413b9b();if(_0x55f5f8&&(_0x55f5f8[_0x5e4fff(0x1f2)]!==_0x5e4fff(0x1b9)||_0x55f5f8['mode']!==_0x5e4fff(0x195)))throw Error(_0x5e4fff(0x1b5));_0x55f5f8&&!(l===_0x5e4fff(0x1cd)&&_0x55f5f8[_0x5e4fff(0x1bf)]===_0x5e4fff(0x197))&&await _0x3c0770();async function _0x53640d(_0x31d607){const _0x35171f=_0x5e4fff;if(_0x31d607==='exit'&&_0x383952(_0x43333d),!_0x55f5f8)return;if(!(await fetch(_0x1ceba5+_0x35171f(0x190),{'method':_0x35171f(0x1cc),'headers':{'x-cu-control':_0x43333d[_0x35171f(0x1ef)](_0x35171f(0x1e4)),'x-cu-stop-mode':_0x31d607},'signal':AbortSignal[_0x35171f(0x17b)](0x1388)}))['ok'])throw Error(_0x35171f(0x1c2));for(let _0x45a321=0x0;_0x45a321<0x32;_0x45a321++){if(!await _0x413b9b()){_0x55f5f8=null;return;}await new Promise(_0x9f5f63=>setTimeout(_0x9f5f63,0x64));}throw Error(_0x35171f(0x186));}if(l===_0x5e4fff(0x1cd)){await _0x53640d(_0x5e4fff(0x183)),console[_0x5e4fff(0x17c)]('Codex\x20Unlimit\x20\u0111\xE3\x20t\u1EAFt.\x20K\u1EBFt\x20n\u1ED1i\x20Codex\x20\u0111\xE3\x20\u0111\u01B0\u1EE3c\x20kh\xF4i\x20ph\u1EE5c.');return;}if(_0x55f5f8&&(l===_0x5e4fff(0x19c)||_0x55f5f8[_0x5e4fff(0x1bf)]!==_0x27bda6)&&(console[_0x5e4fff(0x17c)](_0x5e4fff(0x17e)+_0x27bda6+'\u2026'),await _0x53640d('update')),_0x55f5f8){if(!(await fetch(_0x1ceba5+_0x5e4fff(0x1f8),{'signal':AbortSignal[_0x5e4fff(0x17b)](0x7d0)}))['ok'])throw Error(_0x5e4fff(0x193));}else{let _0x2b8a60=_0x2c6e49[_0x5e4fff(0x18c)](_,_0x5e4fff(0x1cb));_0xabaf0[_0x5e4fff(0x1ee)](_0x2b8a60,{'recursive':!0x0,'mode':0x1c0});let _0x610868=_0xabaf0[_0x5e4fff(0x175)](_0x2c6e49['join'](_0x2b8a60,_0x5e4fff(0x1ba)),'a',0x180),_0x4d410b=_0x5198b6(process[_0x5e4fff(0x1de)],[_0x126536(import.meta.url),_0x5e4fff(0x1a2),_0x5e4fff(0x1c1),_,...n[_0x5e4fff(0x1f1)]('--no-tray')?[_0x5e4fff(0x19e)]:[]],{'detached':!0x0,'windowsHide':!0x0,'stdio':[_0x5e4fff(0x191),_0x610868,_0x610868]}),_0x591e23=!0x1;_0x4d410b[_0x5e4fff(0x1a8)](_0x5e4fff(0x1e1),()=>{_0x591e23=!0x0;}),_0x4d410b[_0x5e4fff(0x1a9)](),_0xabaf0['closeSync'](_0x610868);for(let _0x4077f0=0x0;_0x4077f0<0x64&&(_0x55f5f8=await _0x413b9b(),!(_0x55f5f8||_0x591e23));_0x4077f0++)await new Promise(_0x315020=>setTimeout(_0x315020,0x96));if(!_0x55f5f8)throw Error(_0x5e4fff(0x1a4)+_0x2c6e49[_0x5e4fff(0x18c)](_0x2b8a60,_0x5e4fff(0x1ba)));await _0x3c0770();}if(_0x243133){let _0x3538bc=_0x23347e();try{let _0x17b666=await X({'base':_0x1ceba5,'ticket':_0x4459b4(_0x43333d),'signal':_0x3538bc[_0x5e4fff(0x1d8)]}),_0x12e02a=await _0x1a1495({'api':_0x17b666,'ui':_0x3538bc});_0x12e02a['completed']?_0x43333d[_0x5e4fff(0x1b1)]('setup.completedAt',Date[_0x5e4fff(0x19a)]()):_0x12e02a['cancelled']||(process['exitCode']=0x2);}finally{_0x3538bc[_0x5e4fff(0x1a0)]();}return;}let _0x5e1c9b=_0x1ceba5+_0x5e4fff(0x1b4)+_0x4459b4(_0x43333d);if(console[_0x5e4fff(0x17c)]('Codex\x20Unlimit\x20đang\x20chạy\x20trên\x20máy\x20của\x20bạn.\x0a'+_0x5e1c9b+_0x5e4fff(0x1f4)),!n[_0x5e4fff(0x1f1)](_0x5e4fff(0x185))){let _0x2062d2=process['platform']===_0x5e4fff(0x1d7),_0x5eb90e=_0x2062d2?_0x2c6e49[_0x5e4fff(0x18c)](process.env.SystemRoot??'C:/Windows',_0x5e4fff(0x1d5)):process[_0x5e4fff(0x187)]===_0x5e4fff(0x18d)?_0x5e4fff(0x196):_0x5e4fff(0x1bd),_0x380e2a=_0x2062d2?['-NoProfile',_0x5e4fff(0x1ed),_0x5e4fff(0x1da),_0x5e4fff(0x1e7),'-Command',_0x5e4fff(0x1dc)]:[_0x5e1c9b];try{await new Promise((_0x4dd42e,_0x1b71f9)=>{const _0x5d9157=_0x5e4fff;let _0x10aa0f=_0x5198b6(_0x5eb90e,_0x380e2a,{'windowsHide':!0x0,'stdio':_0x5d9157(0x191),'env':{...process.env,'CU_BROWSER_URL':_0x5e1c9b}}),_0x1eef3a=setTimeout(()=>{const _0xf1442d=_0x5d9157;_0x10aa0f[_0xf1442d(0x1b8)](),_0x1b71f9(Error(_0xf1442d(0x1c8)));},0x3a98);_0x10aa0f[_0x5d9157(0x1a8)](_0x5d9157(0x1e1),_0x54c8ce=>{clearTimeout(_0x1eef3a),_0x1b71f9(_0x54c8ce);}),_0x10aa0f[_0x5d9157(0x1a8)]('exit',_0x4da5b3=>{const _0x46f114=_0x5d9157;clearTimeout(_0x1eef3a),_0x4da5b3===0x0?_0x4dd42e():_0x1b71f9(Error(_0x46f114(0x1dd)));});});}catch{console['error'](_0x5e4fff(0x1f6)),process['exitCode']=0x1;}}}finally{_0x4d1cd2||_0x43333d[_0x5e4fff(0x1a0)]();}}V()[_0xca5444(0x1b7)](_0x33cfde=>{const _0x288c5c=_0xca5444;console[_0x288c5c(0x1e1)](_0x33cfde['code']?_0x3f825a(_0x33cfde):_0x449817[_0x33cfde[_0x288c5c(0x1a3)]]??_0x33cfde[_0x288c5c(0x1a3)]),(l===_0x288c5c(0x179)||_0x33cfde[_0x288c5c(0x18a)]?.['startsWith'](_0x288c5c(0x177)))&&console[_0x288c5c(0x1e1)]('Qu\u1EA3n\x20l\xFD\x20v\xE0\x20ki\u1EC3m\x20tra\x20sau:\x20codexunlimit\x20web'),process[_0x288c5c(0x1f0)]=_0x33cfde[_0x288c5c(0x18a)]===_0x288c5c(0x194)?0x82:0x1;});
@@ -0,0 +1,85 @@
1
+ $ErrorActionPreference = 'Stop'
2
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
3
+ $sessionId = (Get-Process -Id $PID).SessionId
4
+ function Get-DesktopProcesses {
5
+ @(Get-Process -Name ChatGPT,Codex -ErrorAction SilentlyContinue | Where-Object { $_.SessionId -eq $sessionId -and $_.Path })
6
+ }
7
+ function Store-Target([string]$folder) {
8
+ try {
9
+ $manifest = New-Object System.Xml.XmlDocument
10
+ $manifest.XmlResolver = $null
11
+ $manifest.Load((Join-Path $folder 'AppxManifest.xml'))
12
+ if ($manifest.Package.Identity.Name -ne 'OpenAI.Codex') { return $null }
13
+ $application = @($manifest.Package.Applications.Application | Where-Object { $_.Executable -match '(^|[\\/])(ChatGPT|Codex)\.exe$' }) | Select-Object -First 1
14
+ if (!$application -or $application.Id -notmatch '^[A-Za-z0-9._-]+$') { return $null }
15
+ $executable = [IO.Path]::GetFullPath((Join-Path $folder $application.Executable))
16
+ if (!$executable.StartsWith($folder.TrimEnd('\') + '\',[StringComparison]::OrdinalIgnoreCase) -or !(Test-Path -LiteralPath $executable -PathType Leaf)) { return $null }
17
+ $publisher = (Split-Path $folder -Leaf).Split('_')[-1]
18
+ if ($publisher -notmatch '^[a-z0-9]+$') { return $null }
19
+ return @{ executable=$executable; appId=('OpenAI.Codex_' + $publisher + '!' + $application.Id) }
20
+ } catch { return $null }
21
+ }
22
+ function Find-Desktop {
23
+ foreach ($process in (Get-DesktopProcesses)) {
24
+ $prefix = [regex]::Escape((Join-Path $env:ProgramFiles 'WindowsApps'))
25
+ if ($process.Path -match ('^(' + $prefix + '\\OpenAI\.Codex_[^\\]+)\\app\\(?:ChatGPT|Codex)\.exe$')) {
26
+ $target = Store-Target $Matches[1]
27
+ if ($target) { return $target }
28
+ }
29
+ }
30
+ foreach ($package in @(Get-AppxPackage -Name OpenAI.Codex -ErrorAction SilentlyContinue)) {
31
+ $target = Store-Target $package.InstallLocation
32
+ if ($target) { return $target }
33
+ }
34
+ foreach ($candidate in @((Join-Path $env:LOCALAPPDATA 'Programs\Codex\Codex.exe'),(Join-Path $env:LOCALAPPDATA 'OpenAI\Codex\Codex.exe'),(Join-Path $env:ProgramFiles 'Codex\Codex.exe'))) {
35
+ if (Test-Path -LiteralPath $candidate -PathType Leaf) { return @{executable=[IO.Path]::GetFullPath($candidate)} }
36
+ }
37
+ return $null
38
+ }
39
+ function Matching-Processes($target) {
40
+ @(Get-DesktopProcesses | Where-Object { [string]::Equals($_.Path,$target.executable,[StringComparison]::OrdinalIgnoreCase) })
41
+ }
42
+ try {
43
+ $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
44
+ if ($request.action -notin @('status','open','stop')) { throw 'codex_app_invalid_action' }
45
+ $target = Find-Desktop
46
+ if (!$target) {
47
+ if ($request.action -ne 'status') { throw 'codex_app_not_found' }
48
+ @{supported=$true;installed=$false;running=$false} | ConvertTo-Json -Compress
49
+ exit 0
50
+ }
51
+ if ($request.action -eq 'stop') {
52
+ $processes = @(Matching-Processes $target)
53
+ if ($processes.Count -gt 0) {
54
+ $windows = @($processes | Where-Object { $_.MainWindowHandle -ne 0 })
55
+ if ($windows.Count -eq 0) { throw 'codex_app_close_pending' }
56
+ foreach ($process in $windows) { if (!$process.CloseMainWindow()) { throw 'codex_app_close_pending' } }
57
+ $deadline = [DateTime]::UtcNow.AddSeconds(15)
58
+ while (@(Matching-Processes $target).Count -gt 0) {
59
+ if ([DateTime]::UtcNow -ge $deadline) { throw 'codex_app_close_pending' }
60
+ Start-Sleep -Milliseconds 250
61
+ }
62
+ }
63
+ }
64
+ if ($request.action -eq 'open') {
65
+ if ($target.appId) {
66
+ Start-Process -FilePath (Join-Path $env:WINDIR 'explorer.exe') -ArgumentList ('shell:AppsFolder\' + $target.appId) -WindowStyle Hidden
67
+ } else {
68
+ # This is the interactive desktop application explicitly requested by its user.
69
+ Start-Process -FilePath $target.executable -WindowStyle Normal
70
+ }
71
+ $deadline = [DateTime]::UtcNow.AddSeconds(15)
72
+ while (@(Matching-Processes $target).Count -eq 0) {
73
+ if ([DateTime]::UtcNow -ge $deadline) { throw 'codex_app_open_failed' }
74
+ Start-Sleep -Milliseconds 250
75
+ }
76
+ }
77
+ $processes = @(Matching-Processes $target)
78
+ $result = @{supported=$true;installed=$true;running=($processes.Count -gt 0)}
79
+ if ($processes.Count -gt 0) { $first = $processes | Sort-Object StartTime | Select-Object -First 1; $result.startedAt=([DateTimeOffset]$first.StartTime).ToUnixTimeMilliseconds() }
80
+ $result | ConvertTo-Json -Compress
81
+ } catch {
82
+ $code = $_.Exception.Message
83
+ if ($code -notmatch '^codex_app_[a-z_]+$') { $code = 'codex_app_unavailable' }
84
+ @{error=$code} | ConvertTo-Json -Compress
85
+ }
package/package.json CHANGED
@@ -1,14 +1,33 @@
1
- {
2
- "name": "codexunlimit",
3
- "version": "0.1.0-web.12",
4
- "private": false,
5
- "description": "Codex Unlimit — trò chuyện và quản lý kết nối trên máy của bạn",
6
- "type": "module",
7
- "license": "UNLICENSED",
8
- "homepage": "https://codexunlimit.tudonghoa.me",
9
- "publishConfig": {"registry":"https://registry.npmjs.org","access":"public"},
10
- "engines": {"node": "^22.15.0 || >=24.0.0"},
11
- "bin": {"codexunlimit": "cli.mjs"},
12
- "dependencies": {"@openai/codex":"0.166.0","markdown-it":"15.0.3"},
13
- "files": ["app.mjs","account-pool.mjs","codex-account-client.mjs","quota.mjs","cli.mjs","config.mjs","device.ps1","doctor.mjs","image-http.mjs","license-client.mjs","io.mjs","product.mjs","protocol.mjs","relay.mjs","state.mjs","windows.mjs","windows.ps1","updates.mjs","web/","README.md"]
14
- }
1
+ {
2
+ "name": "codexunlimit",
3
+ "version": "0.1.0-web.15",
4
+ "private": false,
5
+ "description": "Codex Unlimit — trò chuyện và quản lý kết nối trên máy của bạn",
6
+ "type": "module",
7
+ "license": "UNLICENSED",
8
+ "homepage": "https://codexunlimit.tudonghoa.me",
9
+ "publishConfig": {
10
+ "registry": "https://registry.npmjs.org",
11
+ "access": "public"
12
+ },
13
+ "engines": {
14
+ "node": "^22.15.0 || >=24.0.0"
15
+ },
16
+ "bin": {
17
+ "codexunlimit": "cli.mjs"
18
+ },
19
+ "dependencies": {
20
+ "@openai/codex": "0.166.0",
21
+ "markdown-it": "15.0.3"
22
+ },
23
+ "files": [
24
+ "cli.mjs",
25
+ "runtime.mjs",
26
+ "shared-CUSGWETD.mjs",
27
+ "codex-desktop.ps1",
28
+ "device.ps1",
29
+ "windows.ps1",
30
+ "web/",
31
+ "README.md"
32
+ ]
33
+ }
package/runtime.mjs ADDED
@@ -0,0 +1 @@
1
+ function _0x2ce3(){const _0x137ab4=['mJveC2zYr1a','q8oIDsb0CUg6OYbS4BUDAsbXDCoHigtdOgKUieJdO3KGy2HPysb5W6P1igpHUQD1ihrOW6bUAcbJW6fJihbO4BQNBIbUAog7JYbOXQfUlG','mteZmJq5odbYvef4rhG','CMvZCg9UC2uUB3v0Chv0x3rLEhqUzgvSDge','DhjPBvn0yxj0','zgf0ytO','mZveEKDXALa','mJe2nZuZm2XvAurirG','s+g6V3qGBUg7KwKGyUg7IYbNACoHBIdeKw/HUQfUihrYXRdHU5TJigTOAsbOB8oGBIb04BQLDc4','mJqYnZi2nhPHtufHuq','zgvSDge','CgfYC2u','CMvWBgfJzq','AM9PBG','y29UDgvUDa','Aw5JBhvKzxm','Aw5KzxHpzG','zMLSDgvY','ugJHUQnUigJHU5nPigTOW7rUzYbO4BUJCcbS4BUhlG','z2v0uMvHzgvY','mJqXotH4DhnZEMC','DhLWzq','C3bSAxq','q2JgSgeGAg/dOg4GDog6PxqGy8oIDsb0CUg6OYbS4BUDAs4GvNvPigZdSM5NihrO4BUTigZHUQfPlG','C3rHCNrZv2L0Aa','C2XPy2u','B3v0Chv0x3rLEhq','mtq2ndi4ogDyrgf1Dq','BgvUz3rO','BwfW','zMXHDe1HCa','C3rYAw5N','mZi1nZiXoufbDKreCa','mtm1odq0Eu11u0Hi','nK55Exvjwq','CMvHza','CMvZCg9UC2uUAw5JB21WBgv0zq','q2JgSgeGBMJHUQ1UimsrXRdHU6nJig7HU5LPigr1BMCGDhlHUQmGBog7NwKUieJdO3KGDgJHU60GBog6OwKU','y2fUy2vS'];_0x2ce3=function(){return _0x137ab4;};return _0x2ce3();}function _0x1106(_0x45ec0c,_0x459399){_0x45ec0c=_0x45ec0c-0x166;const _0x2ce392=_0x2ce3();let _0x11066e=_0x2ce392[_0x45ec0c];if(_0x1106['SJjENB']===undefined){var _0x3a7c31=function(_0x56dea3){const _0x56d26b='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x1f7b74='',_0xf12dfa='';for(let _0x43314c=0x0,_0x8077fb,_0x58b35e,_0x431d15=0x0;_0x58b35e=_0x56dea3['charAt'](_0x431d15++);~_0x58b35e&&(_0x8077fb=_0x43314c%0x4?_0x8077fb*0x40+_0x58b35e:_0x58b35e,_0x43314c++%0x4)?_0x1f7b74+=String['fromCharCode'](0xff&_0x8077fb>>(-0x2*_0x43314c&0x6)):0x0){_0x58b35e=_0x56d26b['indexOf'](_0x58b35e);}for(let _0x3874d4=0x0,_0x534700=_0x1f7b74['length'];_0x3874d4<_0x534700;_0x3874d4++){_0xf12dfa+='%'+('00'+_0x1f7b74['charCodeAt'](_0x3874d4)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0xf12dfa);};_0x1106['dGGyFu']=_0x3a7c31,_0x1106['dZQsNd']={},_0x1106['SJjENB']=!![];}const _0x3b0f83=_0x2ce392[0x0];_0x1106['dbQdOq']!==_0x3b0f83&&(_0x1106['dZQsNd']={},_0x1106['dbQdOq']=_0x3b0f83);const _0x4169b5=_0x1106['dZQsNd'][_0x45ec0c];return _0x4169b5===undefined?(_0x11066e=_0x1106['dGGyFu'](_0x11066e),_0x1106['dZQsNd'][_0x45ec0c]=_0x11066e):_0x11066e=_0x4169b5,_0x11066e;}(function(_0x4f06b3,_0x91b7da){const _0x5ce440=_0x1106,_0x3d8870=_0x4f06b3();while(!![]){try{const _0x4a65b5=parseInt(_0x5ce440(0x168))/0x1*(-parseInt(_0x5ce440(0x17c))/0x2)+parseInt(_0x5ce440(0x183))/0x3+parseInt(_0x5ce440(0x189))/0x4*(parseInt(_0x5ce440(0x16e))/0x5)+-parseInt(_0x5ce440(0x18a))/0x6*(-parseInt(_0x5ce440(0x188))/0x7)+parseInt(_0x5ce440(0x171))/0x8+parseInt(_0x5ce440(0x16f))/0x9+-parseInt(_0x5ce440(0x16a))/0xa;if(_0x4a65b5===_0x91b7da)break;else _0x3d8870['push'](_0x3d8870['shift']());}catch(_0x352247){_0x3d8870['push'](_0x3d8870['shift']());}}}(_0x2ce3,0x4957c));import{A as _0xf12dfa,B as _0x43314c,C as _0x8077fb,D as _0x58b35e,E as _0x431d15,F as _0x3874d4,G as _0x534700,H as _0x1a2650,I as _0x23d155,J as _0x566265,K as _0x3f48f4,L as _0x2f434c,M as _0x34eaac,N as _0x2dbe82,O as _0x2ed6bb,P as _0x5bef29,Q as _0x38504e,R as _0x31a2d1,S as _0x2cb02f,T as _0x3b46ef,U as _0x38cc6c,V as _0x13cc45,W as _0x1cc5d4,X as _0x95623c,Y as _0x5be2bf,a as _0x5c777c,b as _0x1377ca,c as _0x1662a6,d as _0x2036a3,e as _0x23bc68,f as _0x133249,g as _0x29be55,h as _0x2eddfe,i as _0x17c942,j as _0x44ef14,k as _0xa69587,l as _0x3b5811,m as _0x3084f5,n as _0xd69846,o as _0x491a7b,p as _0x14568f,q as _0x37d2ff,r as _0x98d05,s as _0x2533f0,t as _0x182656,u as _0x453407,v as _0x15d425,w as _0x193326,x as _0xf16981,y as _0x4d3775,z as _0x776f5b}from'./shared-CUSGWETD.mjs';async function mt(_0x3a1df7,_0x43e093,{maxText:_0x570676=0x3e800,maxBytes:_0x19b758=0x4*0x400*0x400}={}){const _0x455498=_0x1106;let _0x3d9260=_0x3a1df7[_0x455498(0x17b)](),_0x248937=new TextDecoder(),_0xeb70c9='',_0x542c61=!0x1,_0x2d4cf6='',_0x86d1db=0x0,_0x3b8da4=()=>Error(_0x455498(0x169));function _0x1b6c54(_0x4c8a09){const _0x163645=_0x455498;let _0x574b29=_0x4c8a09[_0x163645(0x17e)]('\x0a')['filter'](_0x2e14ef=>_0x2e14ef[_0x163645(0x180)](_0x163645(0x16d)))[_0x163645(0x185)](_0x3a720b=>_0x3a720b[_0x163645(0x181)](0x5)[_0x163645(0x16c)]())[_0x163645(0x175)]('\x0a');if(!_0x574b29||_0x574b29==='[DONE]')return;let _0x3c0260;try{_0x3c0260=JSON[_0x163645(0x173)](_0x574b29);}catch{throw Error('Ph\u1EA3n\x20h\u1ED3i\x20b\u1ECB\x20gi\xE1n\x20\u0111o\u1EA1n.\x20B\u1EA1n\x20c\xF3\x20th\u1EC3\x20g\u1EEDi\x20l\u1EA1i\x20tin\x20nh\u1EAFn.');}if(_0x3c0260[_0x163645(0x17d)]===_0x163645(0x16b)){if(typeof _0x3c0260['delta']!=_0x163645(0x187))throw Error(_0x163645(0x17a));if(_0x2d4cf6['length']+_0x3c0260[_0x163645(0x172)]['length']>_0x570676)throw _0x3b8da4();_0x2d4cf6+=_0x3c0260[_0x163645(0x172)],_0x43e093(_0x2d4cf6);}if(_0x3c0260[_0x163645(0x17d)]==='response.completed'&&(_0x542c61=!0x0,!_0x2d4cf6)){if(_0x2d4cf6=(_0x3c0260['response']?.['output']??[])[_0x163645(0x186)](_0x74738c=>_0x74738c[_0x163645(0x176)]??[])[_0x163645(0x179)](_0x2290f9=>_0x2290f9['type']===_0x163645(0x182))[_0x163645(0x185)](_0x7a5be6=>_0x7a5be6['text'])[_0x163645(0x175)](''),_0x2d4cf6[_0x163645(0x184)]>_0x570676)throw _0x3b8da4();_0x43e093(_0x2d4cf6);}if(['error','response.failed',_0x163645(0x18c)][_0x163645(0x177)](_0x3c0260[_0x163645(0x17d)]))throw Error(_0x163645(0x17f));}try{for(;;){let {value:_0x305a0f,done:_0x281fd3}=await _0x3d9260[_0x455498(0x18b)]();if(_0x86d1db+=_0x305a0f?.['byteLength']??0x0,_0x86d1db>_0x19b758)throw _0x3b8da4();_0xeb70c9+=_0x248937['decode'](_0x305a0f,{'stream':!_0x281fd3})[_0x455498(0x174)](/\r\n/g,'\x0a'),_0xeb70c9=_0xeb70c9[_0x455498(0x174)](/\r\n/g,'\x0a');let _0x4e8b17;for(;!_0x542c61&&(_0x4e8b17=_0xeb70c9[_0x455498(0x178)]('\x0a\x0a'))>=0x0;)_0x1b6c54(_0xeb70c9['slice'](0x0,_0x4e8b17)),_0xeb70c9=_0xeb70c9['slice'](_0x4e8b17+0x2);if(_0xeb70c9['length']>0x2*0x400*0x400)throw Error('Ph\u1EA3n\x20h\u1ED3i\x20qu\xE1\x20d\xE0i.\x20H\xE3y\x20th\u1EED\x20m\u1ED9t\x20y\xEAu\x20c\u1EA7u\x20ng\u1EAFn\x20h\u01A1n.');if(_0x281fd3||_0x542c61)break;}if(!_0x542c61&&_0xeb70c9['trim']()&&_0x1b6c54(_0xeb70c9),!_0x542c61)throw Error(_0x455498(0x170));if(!_0x2d4cf6)throw Error(_0x455498(0x166));return _0x2d4cf6;}finally{await _0x3d9260[_0x455498(0x167)]()['catch'](()=>{}),_0x3d9260['releaseLock']();}}export{_0x38504e as AccountPool,_0x133249 as ClientState,_0x2f434c as CodexAccountClient,_0x1662a6 as DEFAULT_PORT,_0x2036a3 as LICENSE_ORIGIN,_0x23bc68 as LICENSE_PUBLIC_KEY,_0x2533f0 as LicenseClient,_0x5bef29 as LocalQuota,_0x1377ca as PRODUCT,_0x5c777c as VERSION,_0x34eaac as accountIdentity,_0x491a7b as boundedJson,_0x8077fb as codexConnection,_0x3f48f4 as codexExecutable,_0x3874d4 as compareVersions,_0x1a2650 as createBrowserTicket,_0x2cb02f as createCodexDesktop,_0xf16981 as createConnector,_0x566265 as createLocalApp,_0x1cc5d4 as createTerminalPrompter,_0x95623c as createTerminalSetupClient,_0x534700 as createUpdateChecker,_0x31a2d1 as createWindowsIntegration,_0x3b46ef as diagnoseRuntime,_0x431d15 as errors,_0x15d425 as gatewayRequest,_0x29be55 as hash,_0x2dbe82 as headerWindows,_0x453407 as imageRequest,_0x182656 as isImageRoute,_0x37d2ff as loadDevice,_0x14568f as machineIdentity,_0x4d3775 as managedConfig,_0x58b35e as migrateCodexTransport,_0x3b5811 as proofText,_0x2eddfe as publicText,_0x2ed6bb as rateLimitWindows,_0x23d155 as readCodexAccount,_0x17c942 as readPublic,mt as readResponseStream,_0x98d05 as requestLicense,_0x776f5b as restoreConfig,_0x5be2bf as runTerminalSetup,_0x13cc45 as shouldRunTerminalSetup,_0x44ef14 as signEnvelope,_0x3084f5 as signProof,_0x193326 as signedHeaders,_0xf12dfa as syncCodex,_0x38cc6c as terminalSetupMessage,_0x43314c as unsyncCodex,_0xa69587 as verifyEnvelope,_0xd69846 as verifyProof};