monacloud-mcp 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -4
- package/STATUS.md +40 -0
- package/dist/clients.d.ts +1 -0
- package/dist/clients.js +1 -0
- package/dist/compute.d.ts +1 -1
- package/dist/compute.js +8 -9
- package/dist/jobs.d.ts +2 -0
- package/dist/jobs.js +5 -1
- package/dist/templates.js +1 -1
- package/docs/ai-agent.md +7 -3
- package/docs/wave-ab.md +73 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`monacloud-mcp` là MCP hợp nhất của MONA Cloud: cài một lần, đăng nhập một MONA Pass và dùng chung ví VND để quản lý tài khoản, chạy app/VPS, tích hợp MONA Pay, gửi email giao dịch bằng MONA Mail và đọc catalog MONA Agent ngay trong Claude Code, Codex hoặc Cursor.
|
|
4
4
|
|
|
5
|
-
Human
|
|
5
|
+
Human đăng ký, duyệt chi phí, thêm DNS khi cần, nạp tiền và cung cấp OTP/KYC khi bắt buộc. Các bước tạo tài nguyên, đọc trạng thái, cấu hình webhook, test và deploy được thiết kế để AI agent làm qua MCP.
|
|
6
6
|
|
|
7
7
|
## Yêu cầu
|
|
8
8
|
|
|
@@ -76,7 +76,7 @@ Tên tool dùng `snake_case` để giữ tương thích với prompt MONA Pay c
|
|
|
76
76
|
|
|
77
77
|
### MONA Pay
|
|
78
78
|
|
|
79
|
-
`monacloud-mcp` import `monapay-mcp` và re-export toàn bộ tool của package, không copy implementation. Dependency range là `^0.
|
|
79
|
+
`monacloud-mcp` import `monapay-mcp` và re-export toàn bộ tool của package, không copy implementation. Dependency range là `^0.5.5`; bộ dependency offline tại lần verify này là 0.5.5 với 47 tool MONA Pay, gồm các nhóm:
|
|
80
80
|
|
|
81
81
|
- hồ sơ và nối ACB/VA bằng hai lần OTP;
|
|
82
82
|
- payment profile, checkout, VietQR, sandbox transaction và đối soát;
|
|
@@ -91,7 +91,7 @@ Các tên cũ như `monapay_create_qr`, `monapay_link_bank_start`, `monapay_crea
|
|
|
91
91
|
| Tool | Công dụng |
|
|
92
92
|
|---|---|
|
|
93
93
|
| `cloud_link` | Xác nhận direct MONA Pass; chỉ đổi sang `vc_live_*` nếu upstream còn ở chế độ cũ |
|
|
94
|
-
| `cloud_vps_create` |
|
|
94
|
+
| `cloud_vps_create` | Hourly theo cấu hình; monthly theo `plan_code`, `period: month/year`, kiểm ví đủ giá kỳ |
|
|
95
95
|
| `cloud_db_create` | Tạo MongoDB, PostgreSQL hoặc MySQL |
|
|
96
96
|
| `cloud_job_status` | Poll job tới trạng thái cuối, có timeout |
|
|
97
97
|
| `cloud_services_list` | Liệt kê VPS/database |
|
|
@@ -160,7 +160,7 @@ cloud_services_list({ sandbox: true })
|
|
|
160
160
|
|
|
161
161
|
- `monacloud://llms`: mô tả máy đọc của toàn stack MONA Cloud.
|
|
162
162
|
- `monacloud://status`: health tổng hợp MONA Pass, Billing, compute MONA Cloud, MONA Pay và MONA Mail (`/v1/healthz`).
|
|
163
|
-
- Prompt `dung-app-ban-hang-monacloud`: chuỗi VPS → database → VA/QR → webhook → deploy,
|
|
163
|
+
- Prompt `dung-app-ban-hang-monacloud`: chuỗi VPS → database → VA/QR → webhook → deploy, đọc giá, duyệt chi phí rồi tạo; repo git dùng `cloud_app_create` (đang mở).
|
|
164
164
|
- Prompt `gui-mail-otp-monamail(app_name?, framework?, domain?)`: account → thử onboarding → domain/DNS → verify → API key → `.env` → SDK OTP → webhook bounced; chỉ dừng ở bước thêm DNS hoặc nạp tiền.
|
|
165
165
|
|
|
166
166
|
## Spend guard và lỗi cho AI
|
|
@@ -237,3 +237,33 @@ node --test
|
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
Test dùng Node built-in, MCP transport thật qua stdio, process con và mock HTTP local. Trong sandbox cấm bind socket, test tự dùng fetch fixture tương đương. Bộ test không gọi Internet.
|
|
240
|
+
|
|
241
|
+
## Mới ở 0.3.0: gói tháng, hoá đơn và app từ git
|
|
242
|
+
|
|
243
|
+
| Tool mới | Công dụng |
|
|
244
|
+
|---|---|
|
|
245
|
+
| `cloud_plan_list` | Bảng gói và giá tháng/năm, gợi ý theo CPU/RAM/đĩa |
|
|
246
|
+
| `cloud_subscription_list` / `cloud_subscription_update` | Đọc gói, đổi chu kỳ, auto-renew; huỷ với `cancel_action: hourly/stop` |
|
|
247
|
+
| `cloud_invoice_list` / `cloud_invoice_pdf` | Xem hoá đơn và tải PDF về file tạm riêng tư |
|
|
248
|
+
| `cloud_credit_redeem` | Dùng mã credit được người dùng cung cấp |
|
|
249
|
+
| `cloud_app_create` / `cloud_app_list` / `cloud_app_get` | Deploy public repo HTTPS, poll job và đọc URL; **đang mở** |
|
|
250
|
+
| `cloud_app_deploy` / `cloud_app_env_set` | Deploy lại, thay env; **đang mở** |
|
|
251
|
+
| `cloud_app_domain_add` / `cloud_app_logs` / `cloud_app_delete` | CNAME domain, log build, xoá app; **đang mở** |
|
|
252
|
+
| `cloud_app_host_list` | Xem host và tài nguyên/chi phí; **đang mở** |
|
|
253
|
+
|
|
254
|
+
15 tool mới có 15 alias `vibecloud_` tương ứng. Tổng với MONA Pay 0.5.5 đang có: **133 tool**, gồm 20 Mail và 26 alias compute. Package/MCP binary version: **0.3.0**.
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
cloud_plan_list({ ram_gb: 4 })
|
|
258
|
+
# Đọc ví, báo giá, chờ duyệt rồi tạo:
|
|
259
|
+
cloud_vps_create({ app_name: "shop", billing_mode: "monthly", plan_code: "kinh-doanh", period: "month" })
|
|
260
|
+
cloud_invoice_pdf({ invoice_id: "<id>" })
|
|
261
|
+
cloud_app_host_list()
|
|
262
|
+
cloud_app_create({ repo_url: "https://github.com/example/shop.git", branch: "main", build_type: "nixpacks", sandbox: true })
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Monthly bỏ qua CPU/RAM/đĩa và package_slug, đọc cấu hình/giá từ plan và guard toàn bộ giá tháng/năm. Backend chưa nhận monthly sandbox: MCP thử cấu hình plan qua hourly sandbox 0đ, trả giá thật trong `estimate`, không tạo subscription. Khi tạo thật phải tắt `MONACLOUD_SANDBOX` nếu đã bật env.
|
|
266
|
+
|
|
267
|
+
Khi user nói “deploy repo”, đọc host; chưa có host thì sandbox `cloud_app_create` để ước tính, hỏi duyệt rồi tạo thật và kiểm URL. Tool chờ job tối đa 600 giây; dùng `wait:false` và `cloud_job_status` nếu host MCP có timeout ngắn. Endpoint Wave B đang mở, lỗi API được báo thật.
|
|
268
|
+
|
|
269
|
+
Hợp đồng HTTP, đầy đủ tham số, ví dụ billing/deploy, PDF và giới hạn rollout: [docs/wave-ab.md](docs/wave-ab.md).
|
package/STATUS.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
Updated: 2026-09-05 (Asia/Ho_Chi_Minh)
|
|
4
4
|
|
|
5
|
+
## 0.3.0 — Wave A/B hoàn tất (05/09/2026)
|
|
6
|
+
|
|
7
|
+
- [x] 6 tool billing: `cloud_plan_list`, `cloud_subscription_list`, `cloud_subscription_update`, `cloud_invoice_list`, `cloud_invoice_pdf`, `cloud_credit_redeem`.
|
|
8
|
+
- [x] `cloud_vps_create`/`vibecloud_create_vps` nhận hourly/monthly, plan_code, month/year. Monthly bỏ sizing hourly; estimate và spend guard dùng đúng toàn bộ giá kỳ từ API, gồm gói 0đ.
|
|
9
|
+
- [x] 9 tool app từ git: `cloud_app_create`, `cloud_app_list`, `cloud_app_get`, `cloud_app_deploy`, `cloud_app_env_set`, `cloud_app_domain_add`, `cloud_app_logs`, `cloud_app_delete`, `cloud_app_host_list`. Mô tả VI/EN ghi **đang mở**, gọi endpoint thật qua client; test HTTP mock.
|
|
10
|
+
- [x] 15 alias `vibecloud_` mới, dùng chung schema/handler. Tổng stdio **133 tool = 83 cũ + 20 Mail + 30 Wave A/B**, 2 resource, 2 prompt. Giữ nguyên tích hợp Mail/MONA Pay đang có.
|
|
11
|
+
- [x] App create/deploy poll tối đa 600 giây; trả URL/app_id/application_id, chuẩn hoá `result.estimated_app_host` thành `estimate`. Job error/failed/cancelled trả `app_deploy_failed`; timeout giữ job_id, không POST lại.
|
|
12
|
+
- [x] Sandbox header `X-Vibecloud-Sandbox: 1`, env `MONACLOUD_SANDBOX=1`, bỏ Billing; job poll theo ID. Monthly sandbox thử cấu hình plan qua **hourly sandbox**, báo rõ chưa tạo subscription vì Wave A từ chối monthly sandbox.
|
|
13
|
+
- [x] PDF giữ nguyên binary, file 0600/thư mục tạm 0700, không dùng remote filename hay follow redirect. Lỗi HTTP không tạo file giả.
|
|
14
|
+
- [x] Instructions/resource, prompt dựng app, agent_* và catalog hướng “deploy repo” sang cloud_app_create; đọc → ước tính → duyệt nếu cần → làm, sandbox trước khi chưa có host.
|
|
15
|
+
- [x] MCP và CLI cùng version **0.3.0**. README, [docs/wave-ab.md](docs/wave-ab.md), agent guide và CLI docs có bảng tool/lệnh, ví dụ, giới hạn rollout.
|
|
16
|
+
|
|
17
|
+
Ví dụ gọi:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
cloud_plan_list({ ram_gb: 4 })
|
|
21
|
+
cloud_vps_create({ app_name: "shop", billing_mode: "monthly", plan_code: "kinh-doanh", period: "month" })
|
|
22
|
+
cloud_subscription_update({ service_id: "<id>", auto_renew: false, cancel_action: "stop" })
|
|
23
|
+
cloud_invoice_pdf({ invoice_id: "<id>" })
|
|
24
|
+
cloud_credit_redeem({ code: "<mã đã duyệt>" })
|
|
25
|
+
cloud_app_create({ repo_url: "https://github.com/example/shop.git", build_type: "nixpacks", sandbox: true })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Gate cuối:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
mcp: npm test → 35 tests, 35 pass, 0 fail, 0 skip (bao gồm tsc)
|
|
32
|
+
cli: npm test → 31 tests, 31 pass, 0 fail, 0 skip
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
CLI có test executable → MCP thật → HTTP mock cho plans, VPS monthly, PDF, sandbox, duyệt chi phí và deploy/build lỗi. Stdio xác nhận version 0.3.0, 133 tool và không có stderr chứa secret.
|
|
36
|
+
|
|
37
|
+
Nguồn hợp đồng: snapshot `ctx/openapi.json` vẫn là **0.2.0**, thiếu Wave A/B. Đối chiếu offline schema/router Wave A và Wave B tại repo `vibecloud` trên cùng máy; giữ snapshot gốc và ghi provenance trong docs. Wave B public HTTPS repo, PUT env, query `deployment`, estimate `result.estimated_app_host`. CLI chuyển SSH remote phổ biến sang public HTTPS tương đương, không cấp quyền private repo.
|
|
38
|
+
|
|
39
|
+
Không chạy npm install, không gọi Internet/production, không publish. Toàn bộ phát triển/test trong hai workspace và thư mục tạm. Wave B live chưa kiểm vì brief yêu cầu offline.
|
|
40
|
+
|
|
41
|
+
Kiểm tra đóng gói offline: `npm pack --dry-run --json --offline --ignore-scripts` PASS (MCP 30 file), đủ source build/template/docs mới. Dùng npm CLI trực tiếp với config rỗng và `NODE_USE_SYSTEM_CA=0` để tránh lỗi Keychain/SecItemCopyMatching của Node trên runner. Không tạo tarball và không publish.
|
|
42
|
+
|
|
43
|
+
CODEX DONE
|
|
44
|
+
|
|
5
45
|
## 0.3.0 — MONA Mail
|
|
6
46
|
|
|
7
47
|
- [x] Thêm đủ 20 tool `mail_*` theo CONTRACT-MONA-MAIL-API §5 trong `src/mail.ts`, đăng ký sau nhóm compute `cloud_*`.
|
package/dist/clients.d.ts
CHANGED
package/dist/clients.js
CHANGED
package/dist/compute.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { CloudClients } from './clients.js';
|
|
3
3
|
type Row = Record<string, unknown>;
|
|
4
|
-
export declare const APP_FLOW = "Khi user n\u00F3i \"deploy repo\", d\u00F9ng cloud_app_create (app t\u1EEB git \
|
|
4
|
+
export declare const APP_FLOW = "Khi user n\u00F3i \"deploy repo\", d\u00F9ng cloud_app_create (app t\u1EEB git \u0111\u00E3 live: repo public \u2192 URL https). \u0110\u1ECDc cloud_app_host_list, cloud_prices/cloud_packages v\u00E0 cloud_balance; n\u1EBFu ch\u01B0a c\u00F3 app host th\u00EC cloud_app_create sandbox=true tr\u01B0\u1EDBc \u0111\u1EC3 l\u1EA5y \u01B0\u1EDBc t\u00EDnh. \u0110\u1ECDc \u2192 \u01B0\u1EDBc t\u00EDnh VND \u2192 h\u1ECFi duy\u1EC7t n\u1EBFu ch\u01B0a \u0111\u01B0\u1EE3c duy\u1EC7t \u2192 l\u00E0m th\u1EADt \u2192 poll t\u1EDBi done/succeeded \u2192 ki\u1EC3m URL. Kh\u00F4ng g\u1ECDi agent_deploy cho repo git. / Deploy a repository with cloud_app_create; inspect, estimate, obtain approval, then deploy.";
|
|
5
5
|
export declare function rows(value: unknown, key: string): Row[];
|
|
6
6
|
export declare function planEstimate(clients: CloudClients, code: string, cycle?: string): Promise<{
|
|
7
7
|
plan: Row;
|
package/dist/compute.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { CloudError, runTool } from './errors.js';
|
|
3
3
|
import { unwrapData } from './http.js';
|
|
4
4
|
import { finishAppJob } from './jobs.js';
|
|
5
|
-
export const APP_FLOW = 'Khi user nói "deploy repo", dùng cloud_app_create (app từ git
|
|
5
|
+
export const APP_FLOW = 'Khi user nói "deploy repo", dùng cloud_app_create (app từ git đã live: repo public → URL https). Đọc cloud_app_host_list, cloud_prices/cloud_packages và cloud_balance; nếu chưa có app host thì cloud_app_create sandbox=true trước để lấy ước tính. Đọc → ước tính VND → hỏi duyệt nếu chưa được duyệt → làm thật → poll tới done/succeeded → kiểm URL. Không gọi agent_deploy cho repo git. / Deploy a repository with cloud_app_create; inspect, estimate, obtain approval, then deploy.';
|
|
6
6
|
const sandbox = z.boolean().optional().describe('Thử 0đ, không tạo hạ tầng thật / Sandbox, no charge');
|
|
7
7
|
const id = z.string().trim().min(1).max(255);
|
|
8
8
|
const planCode = z.string().trim().min(2).max(64);
|
|
@@ -70,33 +70,32 @@ export function registerComputeTools(server, clients) {
|
|
|
70
70
|
register('cloud_invoice_list', 'Đọc hoá đơn hàng tháng của tài khoản. / List monthly invoices.', z.object({}).strict(), () => clients.vibecloud('/api/invoices'));
|
|
71
71
|
register('cloud_invoice_pdf', 'Tải PDF hoá đơn vào file tạm riêng tư, trả path; sao chép ra nơi cần giữ trước khi hệ điều hành dọn. / Download invoice PDF to a private temporary file.', z.object({ invoice_id: id }).strict(), ({ invoice_id }) => clients.invoicePdf(invoice_id));
|
|
72
72
|
register('cloud_credit_redeem', 'Dùng mã credit người dùng cung cấp sau khi họ đồng ý; không thử đoán mã. / Redeem an approved promotional credit code.', z.object({ code: z.string().trim().min(2).max(64) }).strict(), ({ code }) => clients.vibecloud('/api/credit-codes/redeem', { method: 'POST', body: { code: code.toUpperCase() } }));
|
|
73
|
-
const appNotice = 'App từ git
|
|
73
|
+
const appNotice = 'App từ git đã live: app host đầu tiên ~2–3 phút, deploy sau đó 10–20 giây. / Git apps are live. ';
|
|
74
74
|
const poll = { wait: z.boolean().default(true), interval_sec: z.number().int().min(1).max(30).default(3), timeout_sec: z.number().int().min(1).max(600).default(600) };
|
|
75
|
-
const env = z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string());
|
|
75
|
+
const env = z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string().refine((value) => !value.includes('\0'), 'Env không chứa NUL.')).refine((value) => Object.keys(value).length <= 200 && Object.entries(value).reduce((sum, [key, val]) => sum + key.length + val.length, 0) <= 65536, 'Env tối đa 200 key / 64 KiB.');
|
|
76
76
|
const domain = z.string().trim().min(3).max(253).regex(/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/, 'Chỉ hostname, không URL/path.');
|
|
77
77
|
const appId = z.object({ app_id: id, sandbox }).strict();
|
|
78
|
+
const gitPath = z.string().trim().min(1).max(255).refine((value) => !/[\x00-\x1f]/.test(value) && !value.startsWith('-') && !value.startsWith('/') && !value.split('/').includes('..'), 'Nhánh/path git không hợp lệ.');
|
|
78
79
|
const repo = z.string().trim().min(1).max(2048).refine((value) => {
|
|
79
|
-
if (/^git@[a-zA-Z0-9.-]+:[^\s]+$/.test(value))
|
|
80
|
-
return true;
|
|
81
80
|
try {
|
|
82
81
|
const u = new URL(value);
|
|
83
|
-
return
|
|
82
|
+
return u.protocol === 'https:' && !u.port && !u.password && !u.username && !u.search && !u.hash && u.pathname.length > 1;
|
|
84
83
|
}
|
|
85
84
|
catch {
|
|
86
85
|
return false;
|
|
87
86
|
}
|
|
88
|
-
}, 'Dùng HTTPS
|
|
87
|
+
}, 'Dùng public HTTPS git URL không chứa token/mật khẩu; private repo chưa được hỗ trợ.');
|
|
89
88
|
const compute = async (path, options, requested) => {
|
|
90
89
|
const isSandbox = clients.sandboxEnabled(requested);
|
|
91
90
|
const result = await clients.vibecloud(path, { ...options, ...(isSandbox ? { headers: { 'X-Vibecloud-Sandbox': '1' } } : {}) });
|
|
92
91
|
return isSandbox ? { ...(Array.isArray(result) ? { items: result } : object(result)), sandbox: true } : result;
|
|
93
92
|
};
|
|
94
|
-
register('cloud_app_create', appNotice + APP_FLOW, z.object({ repo_url: repo, branch:
|
|
93
|
+
register('cloud_app_create', appNotice + APP_FLOW, z.object({ repo_url: repo, branch: gitPath.default('main'), build_type: z.enum(['dockerfile', 'nixpacks', 'static']).default('dockerfile'), dockerfile: gitPath.default('Dockerfile'), env: env.default({}), domain: domain.optional(), app_host_id: id.optional(), port: z.number().int().min(1).max(65535).default(3000), sandbox, ...poll }).strict(), async ({ sandbox: requested, wait, interval_sec, timeout_sec, ...body }) => finishAppJob(clients, await clients.guardedVibecloud('/api/apps', { method: 'POST', body }, requested), clients.sandboxEnabled(requested), wait, interval_sec, timeout_sec));
|
|
95
94
|
register('cloud_app_list', appNotice + 'Liệt kê app trước khi tạo để tránh trùng. / List apps.', z.object({ sandbox }).strict(), ({ sandbox }) => compute('/api/apps', {}, sandbox));
|
|
96
95
|
register('cloud_app_host_list', appNotice + 'Đọc app host; chưa có host thì sandbox cloud_app_create trước để ước tính. / List app hosts.', z.object({ sandbox }).strict(), ({ sandbox }) => compute('/api/app-hosts', {}, sandbox));
|
|
97
96
|
register('cloud_app_get', appNotice + 'Đọc status, URL, lần deploy và app host. / Inspect app.', appId, ({ app_id, sandbox }) => compute(`/api/apps/${encodeURIComponent(app_id)}`, {}, sandbox));
|
|
98
97
|
register('cloud_app_deploy', appNotice + 'Deploy lại sau khi đọc app/log và được duyệt; poll job tới kết quả. / Redeploy and poll.', appId.extend(poll), async ({ app_id, sandbox, wait, interval_sec, timeout_sec }) => finishAppJob(clients, await clients.guardedVibecloud(`/api/apps/${encodeURIComponent(app_id)}/deploy`, { method: 'POST' }, sandbox), clients.sandboxEnabled(sandbox), wait, interval_sec, timeout_sec));
|
|
99
|
-
register('cloud_app_env_set', appNotice + '
|
|
98
|
+
register('cloud_app_env_set', appNotice + 'Thay toàn bộ env sau khi được duyệt, gửi đầy đủ map cần giữ; không log secret. Gọi cloud_app_deploy sau đó để áp dụng. / Set app environment.', appId.extend({ env }), ({ app_id, env, sandbox }) => compute(`/api/apps/${encodeURIComponent(app_id)}/env`, { method: 'PUT', body: { env } }, sandbox));
|
|
100
99
|
register('cloud_app_domain_add', appNotice + 'Thêm domain đã được duyệt, trả hướng dẫn CNAME từ API; chờ DNS trước kiểm HTTPS. / Attach custom domain.', appId.extend({ host: domain }), ({ app_id, host, sandbox }) => compute(`/api/apps/${encodeURIComponent(app_id)}/domains`, { method: 'POST', body: { host } }, sandbox));
|
|
101
100
|
register('cloud_app_logs', appNotice + 'Đọc tối đa 500 dòng log; có thể chứa secret, không đưa nguyên log ra công khai. / Read deployment logs.', appId.extend({ deployment: id.optional() }), ({ app_id, deployment, sandbox }) => compute(`/api/apps/${encodeURIComponent(app_id)}/logs`, { query: { deployment } }, sandbox));
|
|
102
101
|
register('cloud_app_delete', appNotice + 'Sau khi user duyệt xoá: xoá app/domain/A record; app host vẫn có thể tính phí. / Delete an approved app.', appId, ({ app_id, sandbox }) => compute(`/api/apps/${encodeURIComponent(app_id)}`, { method: 'DELETE' }, sandbox));
|
package/dist/jobs.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const TERMINAL_JOBS: Set<string>;
|
|
|
3
3
|
export declare function pollJob(clients: CloudClients, jobId: string, wait: boolean, intervalSeconds: number, timeoutSeconds: number, sandbox: boolean): Promise<Record<string, unknown>>;
|
|
4
4
|
export declare function finishAppJob(clients: CloudClients, created: unknown, sandbox: boolean, wait: boolean, interval: number, timeout: number): Promise<{
|
|
5
5
|
job_id?: string | undefined;
|
|
6
|
+
estimate?: {} | undefined;
|
|
7
|
+
app_id?: string | undefined;
|
|
6
8
|
url?: string | undefined;
|
|
7
9
|
application_id?: string | undefined;
|
|
8
10
|
sandbox?: boolean | undefined;
|
package/dist/jobs.js
CHANGED
|
@@ -5,7 +5,9 @@ export const TERMINAL_JOBS = new Set(['done', 'succeeded', 'failed', 'cancelled'
|
|
|
5
5
|
const object = (value) => (value && typeof value === 'object' ? value : {});
|
|
6
6
|
export async function pollJob(clients, jobId, wait, intervalSeconds, timeoutSeconds, sandbox) {
|
|
7
7
|
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
8
|
-
const read = async () => object(unwrapData(await clients.vibecloud(`/api/jobs/${encodeURIComponent(jobId)}
|
|
8
|
+
const read = async () => object(unwrapData(await clients.vibecloud(`/api/jobs/${encodeURIComponent(jobId)}`, {
|
|
9
|
+
timeoutMs: wait ? Math.max(1, Math.min(30_000, deadline - Date.now())) : 30_000,
|
|
10
|
+
})));
|
|
9
11
|
let result = await read();
|
|
10
12
|
while (wait && !TERMINAL_JOBS.has(String(result.status)) && Date.now() < deadline) {
|
|
11
13
|
await delay(Math.min(intervalSeconds * 1000, Math.max(0, deadline - Date.now())));
|
|
@@ -32,6 +34,8 @@ export async function finishAppJob(clients, created, sandbox, wait, interval, ti
|
|
|
32
34
|
throw new CloudError('app_deploy_failed', `Deploy thất bại${jobId ? ` (job ${jobId})` : ''}: ${job.error || result.errorMessage || result.error || 'kiểm tra log build'}`, 'Gọi cloud_app_logs và cloud_app_get để sửa lỗi; chỉ cloud_app_deploy lại sau khi sửa.');
|
|
33
35
|
}
|
|
34
36
|
return { ...start, ...job, ...(jobId ? { job_id: jobId } : {}),
|
|
37
|
+
...(result.estimated_app_host ? { estimate: result.estimated_app_host } : {}),
|
|
38
|
+
...(typeof result.app_id === 'string' ? { app_id: result.app_id } : {}),
|
|
35
39
|
...(typeof result.url === 'string' ? { url: result.url } : {}),
|
|
36
40
|
...(typeof result.application_id === 'string' ? { application_id: result.application_id } : {}),
|
|
37
41
|
...(sandbox ? { sandbox: true } : {}) };
|
package/dist/templates.js
CHANGED
|
@@ -111,7 +111,7 @@ export class TemplateCatalog {
|
|
|
111
111
|
...template,
|
|
112
112
|
source: 'built-in-wave-1-catalog',
|
|
113
113
|
status: 'catalog_only',
|
|
114
|
-
next_step: 'Catalog runtime chưa được phát hành
|
|
114
|
+
next_step: 'Catalog runtime chưa được phát hành. Deploy repo git: dùng cloud_app_host_list rồi cloud_app_create (đang mở), sandbox trước nếu chưa có app host; báo ước tính và hỏi duyệt trước khi tạo thật.',
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
117
|
}
|
package/docs/ai-agent.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# MONA Cloud MCP — hướng dẫn cho AI agent
|
|
2
2
|
|
|
3
|
-
Tài liệu này dành cho Claude, Codex, Cursor và agent dùng Model Context Protocol. Mục tiêu là hoàn thành tích hợp end-to-end mà không yêu cầu người dùng mở dashboard; các bước human gồm đăng ký MONA Pass, thêm DNS khi cần, nạp tiền và OTP/KYC bắt buộc.
|
|
3
|
+
Tài liệu này dành cho Claude, Codex, Cursor và agent dùng Model Context Protocol. Mục tiêu là hoàn thành tích hợp end-to-end mà không yêu cầu người dùng mở dashboard; các bước human gồm đăng ký MONA Pass, duyệt chi phí, thêm DNS khi cần, nạp tiền và OTP/KYC bắt buộc.
|
|
4
4
|
|
|
5
5
|
## Quy tắc vận hành
|
|
6
6
|
|
|
7
7
|
1. Bắt đầu bằng `cloud_whoami`. Nếu nhận `login_required`, yêu cầu người dùng chạy `monacloud-mcp login`; không hỏi username hoặc password trong chat.
|
|
8
|
-
2. Trước khi provision thật, đọc `cloud_balance`, `
|
|
8
|
+
2. Trước khi provision thật, đọc `cloud_balance`, `cloud_plan_list` hoặc `cloud_packages`/`cloud_prices`; báo ước tính và hỏi duyệt chi phí nếu chưa được duyệt. Với `sandbox: true`, không cần đọc hoặc nạp ví.
|
|
9
9
|
3. Khi cần nạp, gọi `cloud_topup`; đưa nguyên `qr_data_url`, số tiền, nội dung và hạn thanh toán cho người dùng. Chỉ tiếp tục sau khi `cloud_balance` phản ánh tiền vào.
|
|
10
10
|
4. Với OTP ngân hàng, dừng đúng sau `monapay_link_bank_start` và `monapay_notification_register`. Hỏi người dùng mã vừa nhận; không suy đoán, brute-force hoặc ghi OTP vào source/log.
|
|
11
11
|
5. Mọi endpoint webhook phải xác minh HMAC, chống replay theo timestamp và idempotent theo `transaction_code`.
|
|
@@ -119,7 +119,7 @@ Dùng `monapay_create_qr` khi đã có đủ thông tin ACB/VA và cần render
|
|
|
119
119
|
|
|
120
120
|
### 6. Deploy và xác minh
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Với repo git, ưu tiên `cloud_app_create` (đang mở): đọc `cloud_app_host_list`, sandbox trước nếu chưa có host, báo ước tính và duyệt rồi deploy thật. Dùng secret env, chạy migration và kiểm HTTPS/health. Chỉ triển khai VPS thủ công nếu người dùng chọn. Sau đó:
|
|
123
123
|
|
|
124
124
|
- tạo checkout sandbox;
|
|
125
125
|
- tạo transaction sandbox;
|
|
@@ -216,3 +216,7 @@ Sandbox không gọi Proxmox, không cấp IP thật, không trừ tiền và c
|
|
|
216
216
|
## Tiêu chí hoàn tất
|
|
217
217
|
|
|
218
218
|
Một lượt được coi là xong khi app và database chạy, webhook HMAC test pass, sandbox checkout chuyển `paid`, credential nằm trong secret store, và agent báo lại URL cùng trạng thái dịch vụ. Không coi việc “đã tạo job” là hoàn tất; phải poll tới terminal state và kiểm health thực tế.
|
|
219
|
+
|
|
220
|
+
## Wave A/B (0.3.0)
|
|
221
|
+
|
|
222
|
+
Xem [bảng tool và hợp đồng HTTP](wave-ab.md) cho 15 tool mới, VPS monthly, subscriptions, invoices/PDF, credit và app từ git. Khi user nói “deploy repo”, dùng `cloud_app_create`; `agent_deploy` vẫn là stub cho runtime template. Đọc → ước tính → hỏi duyệt nếu chưa được duyệt → làm. Chưa có app host thì sandbox trước, sau khi hoàn tất kiểm URL thật.
|
package/docs/wave-ab.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Wave A/B — gói tháng, hoá đơn và app từ git (0.3.0)
|
|
2
|
+
|
|
3
|
+
Luồng mặc định: **đọc → ước tính VND → hỏi duyệt nếu chưa được duyệt → làm → kiểm kết quả**. Các tool dưới đây đều có alias `vibecloud_` cùng hậu tố, schema và hành vi; riêng alias VPS cũ vẫn là `vibecloud_create_vps`.
|
|
4
|
+
|
|
5
|
+
## Bảng tool
|
|
6
|
+
|
|
7
|
+
| Tool | HTTP compute | Dùng khi |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `cloud_plan_list` | GET `/api/plans` | Xem giá tháng/năm, cấu hình, included_db/backup; gợi ý gói rẻ nhất đủ CPU/RAM/đĩa yêu cầu, không tự chọn admin_only |
|
|
10
|
+
| `cloud_vps_create` | POST `/api/lxc` | Hourly như cũ; monthly nhận `plan_code`, `period: month|year`, trả `estimate` và job |
|
|
11
|
+
| `cloud_subscription_list` | GET `/api/subscriptions` | Đọc gói, kỳ gia hạn, auto-renew |
|
|
12
|
+
| `cloud_subscription_update` | POST `/api/services/{service_id}/subscription` | `plan_code`, `period`, `auto_renew`, `cancel_action: hourly|stop`; ít nhất một thay đổi |
|
|
13
|
+
| `cloud_invoice_list` | GET `/api/invoices` | Đọc hoá đơn tài khoản |
|
|
14
|
+
| `cloud_invoice_pdf` | GET `/api/invoices/{invoice_id}.pdf` | Lưu nguyên bytes vào file tạm 0600 trong thư mục riêng 0700, trả `path` |
|
|
15
|
+
| `cloud_credit_redeem` | POST `/api/credit-codes/redeem` | Body `{code}`, trim/uppercase; không tự đoán mã |
|
|
16
|
+
| `cloud_app_create` | POST `/api/apps` → GET `/api/jobs/{id}` | Deploy public repo HTTPS, tự poll và trả URL; **đang mở** |
|
|
17
|
+
| `cloud_app_list` | GET `/api/apps` | Tránh tạo trùng app; **đang mở** |
|
|
18
|
+
| `cloud_app_get` | GET `/api/apps/{app_id}` | Status, URL, last_deploy, app host; **đang mở** |
|
|
19
|
+
| `cloud_app_deploy` | POST `/api/apps/{app_id}/deploy` → job | Deploy lại sau sửa code/env; **đang mở** |
|
|
20
|
+
| `cloud_app_env_set` | PUT `/api/apps/{app_id}/env` | Body `{env: {KEY: "value"}}`, thay toàn bộ env; deploy lại để áp dụng; **đang mở** |
|
|
21
|
+
| `cloud_app_domain_add` | POST `/api/apps/{app_id}/domains` | Body `{host}`, trả hướng dẫn CNAME; **đang mở** |
|
|
22
|
+
| `cloud_app_logs` | GET `/api/apps/{app_id}/logs?deployment=...` | Tối đa 500 dòng, giữ log có secret riêng tư; **đang mở** |
|
|
23
|
+
| `cloud_app_delete` | DELETE `/api/apps/{app_id}` | Xoá app/domain/A record sau khi được duyệt; host vẫn có thể tính phí; **đang mở** |
|
|
24
|
+
| `cloud_app_host_list` | GET `/api/app-hosts` | Đọc host, tài nguyên và phí; **đang mở** |
|
|
25
|
+
|
|
26
|
+
## Gói tháng/năm
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
cloud_plan_list({ ram_gb: 4 })
|
|
30
|
+
cloud_balance()
|
|
31
|
+
# Báo đúng price_month_vnd hoặc price_year_vnd, hỏi duyệt nếu chưa có.
|
|
32
|
+
cloud_vps_create({ app_name: "shop", billing_mode: "monthly", plan_code: "kinh-doanh", period: "month" })
|
|
33
|
+
cloud_job_status({ job_id: "<id trả về>" })
|
|
34
|
+
cloud_subscription_list()
|
|
35
|
+
cloud_subscription_update({ service_id: "<service_id>", auto_renew: false, cancel_action: "stop" })
|
|
36
|
+
cloud_invoice_list()
|
|
37
|
+
cloud_invoice_pdf({ invoice_id: "<invoice_id>" })
|
|
38
|
+
cloud_credit_redeem({ code: "<mã người dùng cung cấp>" })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Monthly bỏ `package_slug`, CPU/RAM/đĩa trong payload trước khi gửi, lấy cấu hình từ plan. Spend guard so sánh số dư với **toàn bộ giá kỳ đã chọn**, không chỉ số dư dương. Plan giá 0 được phép với ví 0; API vẫn kiểm quyền gói admin. Giá đọc từ API, không hard-code. Nâng gói tính prorate và hạ gói kỳ sau do backend quyết định; MCP không tự tính khoản prorate hoặc chặn huỷ khi ví hết tiền.
|
|
42
|
+
|
|
43
|
+
Wave A hiện từ chối monthly trong sandbox. `cloud_vps_create` monthly + sandbox lấy CPU/RAM/đĩa từ plan, gửi request **hourly sandbox** với `X-Vibecloud-Sandbox: 1`; trả `estimate` là giá gói thật, `requested_billing_mode: monthly`, `preview_billing_mode: hourly`. Không tạo subscription và không trừ tiền. Được duyệt rồi mới gọi monthly thật, tắt cả tham số sandbox và `MONACLOUD_SANDBOX` nếu env đang bật.
|
|
44
|
+
|
|
45
|
+
PDF là file tạm trên máy chạy MCP, không phải máy từ xa của người chat. Sao chép file nếu cần giữ lâu; hệ điều hành có thể dọn thư mục tạm. Không dùng filename từ server làm đường dẫn local, không follow redirect có bearer token, không chuyển PDF qua text/JSON.
|
|
46
|
+
|
|
47
|
+
## Deploy repo: 3 bước
|
|
48
|
+
|
|
49
|
+
1. Đọc repo/nhánh/build/domain, `cloud_app_list`, `cloud_app_host_list`, `cloud_prices`, `cloud_packages`. Nếu chưa có host, gọi `cloud_app_create` sandbox trước để lấy `result.estimated_app_host` (MCP cũng trả thành `estimate`) và URL thử. Đọc `cloud_balance` trước tạo thật.
|
|
50
|
+
2. Báo chi phí host, repo, nhánh và domain, hỏi duyệt nếu chưa được duyệt. Gói tháng cần giá đúng kỳ; app dùng host hiện có vẫn tiếp tục phí host. Sandbox không cần ví, không cần duyệt chi phí.
|
|
51
|
+
3. `cloud_app_create` thật, chờ terminal job, đọc `cloud_app_get`/`cloud_app_logs`, kiểm HTTPS/health rồi báo URL. Khi có host, truyền `app_host_id` để dùng lại. Custom domain dùng `cloud_app_domain_add`, làm theo CNAME và chờ SSL.
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
cloud_app_create({ repo_url: "https://github.com/example/shop.git", branch: "main", build_type: "nixpacks", port: 3000, env: {}, sandbox: true })
|
|
55
|
+
# Đọc estimate, duyệt, rồi:
|
|
56
|
+
cloud_app_create({ repo_url: "https://github.com/example/shop.git", branch: "main", build_type: "nixpacks", port: 3000, env: {}, sandbox: false })
|
|
57
|
+
cloud_app_env_set({ app_id: "<app_id>", env: { NODE_ENV: "production" } })
|
|
58
|
+
cloud_app_deploy({ app_id: "<app_id>" })
|
|
59
|
+
cloud_app_domain_add({ app_id: "<app_id>", host: "shop.example.vn" })
|
|
60
|
+
cloud_app_logs({ app_id: "<app_id>", deployment: "<deployment_id>" })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`cloud_app_create` mặc định branch=`main`, build_type=`dockerfile`, dockerfile=`Dockerfile`, env=`{}`, port=`3000`, wait=`true`, interval_sec=`3`, timeout_sec=`600`. Build có `dockerfile|nixpacks|static`. `wait: false` trả job để client tự poll; nên dùng nếu MCP host giới hạn mỗi tool dưới 10 phút. `done`/`succeeded` là hoàn tất; `failed`/`error`/`cancelled` trả lỗi `app_deploy_failed`. Timeout giữ job_id và next_step để poll tiếp; không tự gửi lại POST.
|
|
64
|
+
|
|
65
|
+
Wave B hiện nhận **public HTTPS repo** không credential/query/fragment. CLI đổi remote `git@host:owner/repo.git` hoặc `ssh://git@host/owner/repo.git` sang HTTPS; điều đó không cấp quyền repo private. Không đưa token vào URL. Env là map string, tối đa 200 key/64 KiB; không in secret hoặc log build ra công khai.
|
|
66
|
+
|
|
67
|
+
Sandbox app dùng `X-Vibecloud-Sandbox: 1` cho các endpoint app. Job được tìm theo ID; không cần header khi poll. `MONACLOUD_SANDBOX=1` cũng bật sandbox và không thể bị `sandbox:false` ghi đè. Lỗi 404/503 khi endpoint chưa mở được trả nguyên theo API, không có thành công giả.
|
|
68
|
+
|
|
69
|
+
Prompt mẫu: “Deploy repo hiện tại lên MONA Cloud. Kiểm host và giá, sandbox trước nếu chưa có host, báo chi phí để tôi duyệt rồi deploy thật và kiểm URL.”
|
|
70
|
+
|
|
71
|
+
## Nguồn hợp đồng offline
|
|
72
|
+
|
|
73
|
+
Snapshot `ctx/openapi.json` được giao vẫn ghi API `0.2.0`, chưa có Wave A/B. Đã đối chiếu **file local** `vibecloud/app/schemas.py`, `app/routers/api.py` cho Wave A; Wave B theo `ctx/BRIEF-CODEX-WAVE-B-APPS.md` §2–4 và đối chiếu thêm `vibecloud/app/routers/apps.py`, `app/sandbox.py`. Vì brief cũ trong `SPEC-WAVE-B-APP-TU-GIT.md` ghi POST env và `?deploy=`, implementation dùng hợp đồng mới hơn: **PUT env, `?deployment=`**. Không sửa snapshot cũ để tránh gán nhầm provenance; không gọi endpoint production.
|