@nextxai/cli 0.3.4 → 0.3.5
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/CHANGELOG.md +4 -0
- package/dist/ai/local.js +1 -1
- package/dist/commands/ai.js +3 -3
- package/dist/commands/api.js +1 -1
- package/dist/commands/auth.js +15 -15
- package/dist/commands/ci-policy.js +3 -3
- package/dist/commands/ci.js +4 -4
- package/dist/commands/keys.js +2 -2
- package/dist/commands/model.js +3 -3
- package/dist/commands/tools.js +1 -1
- package/dist/commands/webhooks.js +7 -7
- package/dist/core/i18n.js +8 -8
- package/dist/program.js +4 -4
- package/dist/repl/App.js +5 -5
- package/dist/repl/slash.js +13 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.5
|
|
4
|
+
|
|
5
|
+
- Vietnamese help keeps technical terms in English (API key, session, scope, signature, secret, retry, exit code, permission mode, output format, sandbox), and argument placeholders are English everywhere (`/rename <name>`, `/attach <file>`, `/config [key value]`).
|
|
6
|
+
|
|
3
7
|
## 0.3.4
|
|
4
8
|
|
|
5
9
|
- Every file in a GitHub release now has a signed build provenance attestation: `gh attestation verify <file> --repo <owner/repo>`. Release notes list the install commands and how to verify a download.
|
package/dist/ai/local.js
CHANGED
|
@@ -270,7 +270,7 @@ function providerError(m, status, message) {
|
|
|
270
270
|
if (status === 404)
|
|
271
271
|
return new CliError(tr(`${who}: không có model này.`, `${who}: no such model.`) + (message ? ` (${message})` : ""), Exit.notFound);
|
|
272
272
|
if (status === 429)
|
|
273
|
-
return new CliError(tr(`${who}: nhà model đang
|
|
273
|
+
return new CliError(tr(`${who}: nhà model đang rate limit hoặc hết hạn mức.`, `${who}: the provider is rate limiting or out of quota.`) + (message ? ` (${message})` : ""), Exit.quota);
|
|
274
274
|
return new CliError(`${who}: HTTP ${status}${message ? ` ${message}` : ""}`, Exit.general);
|
|
275
275
|
}
|
|
276
276
|
function anthropicError(e, signal) {
|
package/dist/commands/ai.js
CHANGED
|
@@ -126,7 +126,7 @@ export async function handleProposal(sessionId, a, mode = context().mode) {
|
|
|
126
126
|
try {
|
|
127
127
|
const r = await confirmAction(sessionId, a);
|
|
128
128
|
if (r.success)
|
|
129
|
-
ok(r.message || tr("Đã ghi (CI, trong
|
|
129
|
+
ok(r.message || tr("Đã ghi (CI, trong scope duyệt trước).", "Written (CI, within the pre-approved scope)."));
|
|
130
130
|
else
|
|
131
131
|
warn(r.message);
|
|
132
132
|
return { written: r.success, message: r.message };
|
|
@@ -348,7 +348,7 @@ async function findChangeset(id) {
|
|
|
348
348
|
return hit;
|
|
349
349
|
}
|
|
350
350
|
export function registerAi(program) {
|
|
351
|
-
const ai = program.command("ai").description(tr("trợ lý AI: hỏi, lịch sử,
|
|
351
|
+
const ai = program.command("ai").description(tr("trợ lý AI: hỏi, lịch sử, write proposal, bộ nhớ, lịch chạy", "AI assistant: ask, history, proposals, memory, schedules"));
|
|
352
352
|
ai.command("ask <question...>")
|
|
353
353
|
.description(tr("hỏi một câu không tương tác (giống -p)", "ask one question non-interactively (same as -p)"))
|
|
354
354
|
.option("--conversation <id>", tr("hỏi tiếp trong hội thoại này", "continue this conversation"))
|
|
@@ -454,7 +454,7 @@ export function registerAi(program) {
|
|
|
454
454
|
throw new CliError(tr("Chế độ chỉ đọc.", "Read-only mode."), Exit.forbidden);
|
|
455
455
|
const ci = !hasTty() && (await isCiSession());
|
|
456
456
|
if (!hasTty() && !ci)
|
|
457
|
-
throw new CliError(tr("Áp dụng bản nháp cần terminal tương tác (hoặc đăng nhập CI có
|
|
457
|
+
throw new CliError(tr("Áp dụng bản nháp cần terminal tương tác (hoặc đăng nhập CI có scope duyệt trước).", "Applying needs an interactive terminal (or a CI login with a pre-approved scope)."), Exit.notConfirmed);
|
|
458
458
|
const env = context().env === "prod" ? c.red("PROD") : c.yellow("SANDBOX");
|
|
459
459
|
info(`${c.bold(head.title)} · ${env}`);
|
|
460
460
|
if (!ci) {
|
package/dist/commands/api.js
CHANGED
|
@@ -33,7 +33,7 @@ export function registerApi(program) {
|
|
|
33
33
|
.option("-H, --header <K: V>", tr("thêm header (lặp được)", "extra header (repeatable)"), (v, p = []) => [...p, v])
|
|
34
34
|
.option("--paginate", tr("đi hết các trang (kết quả dạng items/pageNumber)", "follow all pages (items/pageNumber results)"))
|
|
35
35
|
.option("--select <path>", tr("chỉ in một phần, vd .items[0].name", "print only part of the result, e.g. .items[0].name"))
|
|
36
|
-
.option("-i, --include", tr("in cả
|
|
36
|
+
.option("-i, --include", tr("in cả status code và header", "print status and headers too"))
|
|
37
37
|
.option("--idempotency-key <key>", tr("khoá chống trùng cho POST", "idempotency key for POST"))
|
|
38
38
|
.action(async (methodRaw, path, o) => {
|
|
39
39
|
const method = methodRaw.toUpperCase();
|
package/dist/commands/auth.js
CHANGED
|
@@ -31,7 +31,7 @@ function printLogin(r) {
|
|
|
31
31
|
const p = getProfile(context().profile);
|
|
32
32
|
ok(tr(`Đã đăng nhập ${c.bold(r.userName ?? "")} (${r.userEmail ?? ""})`, `Logged in as ${c.bold(r.userName ?? "")} (${r.userEmail ?? ""})`));
|
|
33
33
|
info(` ${tr("Workspace", "Workspace")} ${c.bold(r.workspaceName ?? p?.workspaceName ?? "")} · ${context().env === "prod" ? "prod" : c.yellow("sandbox")}`);
|
|
34
|
-
info(` ${tr("
|
|
34
|
+
info(` ${tr("Session", "Session")} ${r.session.via === "ci" ? tr("15 phút, không gia hạn — hết thì đăng nhập CI lại", "15 minutes, no renewal — logs in again through CI when it ends") : tr("tự gia hạn khi dùng, hết hạn nếu bỏ không 30 ngày", "renews while used, expires after 30 idle days")} · ${storageKind() === "keychain" ? "Keychain" : tr("tệp 0600", "0600 file")}`);
|
|
35
35
|
info(c.dim(` ${tr("Thử", "Try")}: ${brand().bin} · ${brand().bin} -p "${tr(brand().example.vi, brand().example.en)}" · ${brand().bin} help`));
|
|
36
36
|
}
|
|
37
37
|
export function registerAuth(program) {
|
|
@@ -49,7 +49,7 @@ export function registerAuth(program) {
|
|
|
49
49
|
let r;
|
|
50
50
|
if ((o.ci && b.notYet?.includes("ci")) || (o.device && b.notYet?.includes("device"))) {
|
|
51
51
|
const { notYetError } = await import("../core/errors.js");
|
|
52
|
-
throw notYetError(tr(`${b.name} chưa mở ${o.ci ? "đăng nhập CI bằng OIDC" : "đăng nhập bằng mã thiết bị"}`, `${b.name} does not offer ${o.ci ? "CI OIDC login" : "device-code login"} yet`), o.ci ? tr(`dùng
|
|
52
|
+
throw notYetError(tr(`${b.name} chưa mở ${o.ci ? "đăng nhập CI bằng OIDC" : "đăng nhập bằng mã thiết bị"}`, `${b.name} does not offer ${o.ci ? "CI OIDC login" : "device-code login"} yet`), o.ci ? tr(`dùng API key: ${b.envPrefix}_API_KEY`, `use an API key: ${b.envPrefix}_API_KEY`) : tr(`dùng ${b.bin} login --qr (quét bằng app ${b.mobileApp}) hoặc --no-browser`, `use ${b.bin} login --qr (scan with the ${b.mobileApp} app) or --no-browser`));
|
|
53
53
|
}
|
|
54
54
|
if (o.ci)
|
|
55
55
|
r = await loginCi({ workspace: ws });
|
|
@@ -68,7 +68,7 @@ export function registerAuth(program) {
|
|
|
68
68
|
});
|
|
69
69
|
program
|
|
70
70
|
.command("logout")
|
|
71
|
-
.description(tr("đăng xuất hồ sơ này (thu hồi
|
|
71
|
+
.description(tr("đăng xuất hồ sơ này (thu hồi session trên máy chủ)", "log out this profile (revokes the server session)"))
|
|
72
72
|
.option("--all", tr("đăng xuất mọi hồ sơ trên máy này", "log out every profile on this machine"))
|
|
73
73
|
.action(async (o) => {
|
|
74
74
|
const profiles = o.all ? Object.keys(loadConfig().profiles) : [context().profile];
|
|
@@ -86,12 +86,12 @@ export function registerAuth(program) {
|
|
|
86
86
|
if (revoked)
|
|
87
87
|
ok(tr(`Đã đăng xuất (${name})`, `Logged out (${name})`));
|
|
88
88
|
else
|
|
89
|
-
warn(tr(`Đã xoá
|
|
89
|
+
warn(tr(`Đã xoá session trên máy (${name}), nhưng không tới được máy chủ: session còn sống tới khi hết hạn. Thu hồi tại ${context().endpoints.id} › Thiết bị`, `Removed the local session (${name}), but the server was unreachable: it stays valid until it expires. Revoke it at ${context().endpoints.id} › Devices`));
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
92
|
program
|
|
93
93
|
.command("status")
|
|
94
|
-
.description(tr("tài khoản, workspace, môi trường, gói AI,
|
|
94
|
+
.description(tr("tài khoản, workspace, môi trường, gói AI, session", "account, workspace, environment, AI plan, session"))
|
|
95
95
|
.action(async () => {
|
|
96
96
|
const src = authSource();
|
|
97
97
|
const p = getProfile(context().profile);
|
|
@@ -105,7 +105,7 @@ export function registerAuth(program) {
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
if (src === "apiKey") {
|
|
108
|
-
emit({ ...base, loggedIn: true, kind: "apiKey" }, () => info(tr(`Đang dùng
|
|
108
|
+
emit({ ...base, loggedIn: true, kind: "apiKey" }, () => info(tr(`Đang dùng API key từ ${b.envPrefix}_API_KEY (danh tính agent).`, `Using the API key from ${b.envPrefix}_API_KEY (agent identity).`)));
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
111
|
const auth = await currentAuth({ required: true });
|
|
@@ -129,14 +129,14 @@ export function registerAuth(program) {
|
|
|
129
129
|
info(` ${tr("Môi trường", "Environment")} ${context().env === "prod" ? c.red("prod") : c.yellow("sandbox")} · ${context().endpoints.api}`);
|
|
130
130
|
if (u)
|
|
131
131
|
info(` ${tr("Gói AI", "AI plan")} ${u.planName ?? "-"}${u.limit ? ` · ${tr("đã dùng", "used")} ${u.used}/${u.limit}, ${tr("mở lại", "resets")} ${u.resetsAt?.slice(0, 10)}` : ""}`);
|
|
132
|
-
info(` ${tr("Danh tính", "Identity")} ${src === "token" ? `${b.envPrefix}_TOKEN` : `${tr("
|
|
132
|
+
info(` ${tr("Danh tính", "Identity")} ${src === "token" ? `${b.envPrefix}_TOKEN` : `${tr("session", "session")} (${s?.via}) · ${storageKind() === "keychain" ? "Keychain" : tr("tệp", "file")}`}`);
|
|
133
133
|
info(` ${tr("Hồ sơ", "Profile")} ${context().profile}${p?.agent ? ` · agent ${p.agent}` : ""} · CLI ${VERSION}`);
|
|
134
134
|
});
|
|
135
135
|
});
|
|
136
|
-
const sessions = program.command("sessions").description(tr("
|
|
136
|
+
const sessions = program.command("sessions").description(tr("session của bạn trên mọi thiết bị", "your login sessions on all devices"));
|
|
137
137
|
sessions
|
|
138
138
|
.command("list", { isDefault: true })
|
|
139
|
-
.description(tr("danh sách
|
|
139
|
+
.description(tr("danh sách session", "list sessions"))
|
|
140
140
|
.action(async () => {
|
|
141
141
|
const list = await get("/iam/sessions");
|
|
142
142
|
const device = (s) => {
|
|
@@ -149,21 +149,21 @@ export function registerAuth(program) {
|
|
|
149
149
|
});
|
|
150
150
|
sessions
|
|
151
151
|
.command("revoke [id]")
|
|
152
|
-
.description(tr("thu hồi một
|
|
153
|
-
.option("--others", tr("mọi
|
|
152
|
+
.description(tr("thu hồi một session, hoặc --others để thu hồi mọi session khác", "revoke one session, or --others for all other sessions"))
|
|
153
|
+
.option("--others", tr("mọi session trừ session này", "every session except this one"))
|
|
154
154
|
.action(async (id, o) => {
|
|
155
155
|
if (o.others) {
|
|
156
156
|
await del("/iam/sessions");
|
|
157
|
-
ok(tr("Đã thu hồi mọi
|
|
157
|
+
ok(tr("Đã thu hồi mọi session khác.", "Revoked all other sessions."));
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
160
|
if (!id)
|
|
161
|
-
throw new CliError(tr("Cần id
|
|
161
|
+
throw new CliError(tr("Cần id session (xem `sessions list`) hoặc --others.", "Pass a session id (see `sessions list`) or --others."), Exit.usage);
|
|
162
162
|
const list = await get("/iam/sessions");
|
|
163
163
|
const hit = list.find((s) => s.id === id || s.id.startsWith(id));
|
|
164
164
|
if (!hit)
|
|
165
|
-
throw new CliError(tr("Không thấy
|
|
165
|
+
throw new CliError(tr("Không thấy session này.", "Session not found."), Exit.notFound);
|
|
166
166
|
await del(`/iam/sessions/${hit.id}`);
|
|
167
|
-
ok(tr(`Đã thu hồi
|
|
167
|
+
ok(tr(`Đã thu hồi session ${hit.id.slice(0, 8)}.`, `Revoked session ${hit.id.slice(0, 8)}.`));
|
|
168
168
|
});
|
|
169
169
|
}
|
|
@@ -62,13 +62,13 @@ export function registerCiPolicy(ci) {
|
|
|
62
62
|
});
|
|
63
63
|
});
|
|
64
64
|
policy.command("request")
|
|
65
|
-
.description(tr("xin cho một kho CI tự ghi (người quản lý
|
|
66
|
-
.requiredOption("--trust <id|owner/repo>", tr("
|
|
65
|
+
.description(tr("xin cho một kho CI tự ghi (người quản lý API key duyệt; có quyền thì có hiệu lực ngay)", "ask to let a CI repository write (an API-key manager approves; with that permission it is active at once)"))
|
|
66
|
+
.requiredOption("--trust <id|owner/repo>", tr("CI trust (xem: ci trust list)", "CI trust (see: ci trust list)"))
|
|
67
67
|
.requiredOption("--kinds <a,b>", tr("loại ghi (xem: ci policy kinds)", "kinds (see: ci policy kinds)"))
|
|
68
68
|
.option("--max <n>", tr("trần số bản ghi (1–1000)", "record ceiling (1–1000)"), "50")
|
|
69
69
|
.option("--expires <dur>", tr("thời hạn tối đa 30d", "lifetime, at most 30d"), "14d")
|
|
70
70
|
.option("--note <text>")
|
|
71
|
-
.option("--approve", tr("duyệt luôn (cần quyền quản lý
|
|
71
|
+
.option("--approve", tr("duyệt luôn (cần quyền quản lý API key)", "approve now (needs API-key manage permission)"))
|
|
72
72
|
.action(async (o) => {
|
|
73
73
|
const want = o.trust.trim().toLowerCase();
|
|
74
74
|
const trust = (await get("/iam/ci-trusts")).find((t) => !t.revokedAt && (t.id.startsWith(want) || t.repository === want));
|
package/dist/commands/ci.js
CHANGED
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
export function workflowWriteSample(repo = "acme/web", ws = "my-workspace") {
|
|
32
32
|
const b = brand();
|
|
33
33
|
return `# .github/workflows/follow-up.yml (${repo})
|
|
34
|
-
# ${tr("Ghi chỉ trong chính sách duyệt trước (ci policy request); ngoài
|
|
34
|
+
# ${tr("Ghi chỉ trong chính sách duyệt trước (ci policy request); ngoài scope thì để lại đề xuất chờ người duyệt (exit code 7).", "Writes only within an approved policy (ci policy request); anything else stays a proposal for a person (exit code 7).")}
|
|
35
35
|
permissions:
|
|
36
36
|
id-token: write
|
|
37
37
|
contents: read
|
|
@@ -43,12 +43,12 @@ jobs:
|
|
|
43
43
|
- run: npx -y ${b.pkg} -p "${tr("tạo việc gọi lại cho khách quá hạn chăm sóc 7 ngày", "create call-back tasks for customers not contacted in 7 days")}" --mode auto`;
|
|
44
44
|
}
|
|
45
45
|
export function registerCi(program) {
|
|
46
|
-
const ci = program.command("ci").description(tr("đăng nhập từ CI không cần
|
|
46
|
+
const ci = program.command("ci").description(tr("đăng nhập từ CI không cần secret (OIDC)", "secretless CI login (OIDC)"));
|
|
47
47
|
const trust = ci.command("trust").description(tr("kho CI được phép đăng nhập workspace này", "CI repositories allowed to log in to this workspace"));
|
|
48
48
|
trust.hook("preAction", async () => {
|
|
49
49
|
if (brand().notYet?.includes("ci")) {
|
|
50
50
|
const { notYetError } = await import("../core/errors.js");
|
|
51
|
-
throw notYetError(tr(`${brand().name} chưa mở đăng nhập CI bằng OIDC`, `${brand().name} does not offer CI OIDC login yet`), tr(`dùng
|
|
51
|
+
throw notYetError(tr(`${brand().name} chưa mở đăng nhập CI bằng OIDC`, `${brand().name} does not offer CI OIDC login yet`), tr(`dùng API key: ${brand().envPrefix}_API_KEY`, `use an API key: ${brand().envPrefix}_API_KEY`));
|
|
52
52
|
}
|
|
53
53
|
await requirePerson();
|
|
54
54
|
});
|
|
@@ -75,7 +75,7 @@ export function registerCi(program) {
|
|
|
75
75
|
});
|
|
76
76
|
emit(t, () => {
|
|
77
77
|
ok(tr(`Đã cho ${t.provider}:${t.repository} (${t.refPattern}) đăng nhập`, `Allowed ${t.provider}:${t.repository} (${t.refPattern})`));
|
|
78
|
-
info(c.dim(tr(`\nCho CI tự ghi trong trần: ${brand().bin} ci policy request --trust ${t.repository} --kinds <
|
|
78
|
+
info(c.dim(tr(`\nCho CI tự ghi trong trần: ${brand().bin} ci policy request --trust ${t.repository} --kinds <code>`, `\nLet CI write within a ceiling: ${brand().bin} ci policy request --trust ${t.repository} --kinds <code>`)));
|
|
79
79
|
info(c.dim(tr("\nMẫu workflow:", "\nSample workflow:")));
|
|
80
80
|
info(workflowSample(t.repository));
|
|
81
81
|
});
|
package/dist/commands/keys.js
CHANGED
|
@@ -22,7 +22,7 @@ async function findKey(id) {
|
|
|
22
22
|
}
|
|
23
23
|
const scopeExample = () => brand().examples.scopes;
|
|
24
24
|
export function registerKeys(program) {
|
|
25
|
-
const keys = program.command("keys").description(tr("
|
|
25
|
+
const keys = program.command("keys").description(tr("API key của workspace", "workspace API keys"));
|
|
26
26
|
keys.hook("preAction", async () => void (await requirePerson()));
|
|
27
27
|
keys.command("list", { isDefault: true }).description(tr("danh sách khoá", "list keys")).action(async () => {
|
|
28
28
|
const list = await get("/iam/api-keys");
|
|
@@ -49,7 +49,7 @@ export function registerKeys(program) {
|
|
|
49
49
|
emit(r, () => {
|
|
50
50
|
ok(tr(`Đã tạo khoá "${o.name}"`, `Created key "${o.name}"`));
|
|
51
51
|
info(`\n ${c.bold(raw)}\n`);
|
|
52
|
-
warn(tr("Chỉ hiện một lần. Lưu ngay vào nơi an toàn (biến
|
|
52
|
+
warn(tr("Chỉ hiện một lần. Lưu ngay vào nơi an toàn (biến secret của CI, trình quản lý mật khẩu).", "Shown only once. Store it now (CI secret, password manager)."));
|
|
53
53
|
info(c.dim(` ${brand().envPrefix}_API_KEY=${raw.slice(0, 8)}… ${brand().bin} tools list`));
|
|
54
54
|
});
|
|
55
55
|
});
|
package/dist/commands/model.js
CHANGED
|
@@ -66,8 +66,8 @@ export function registerModel(program) {
|
|
|
66
66
|
model
|
|
67
67
|
.command("add <provider>")
|
|
68
68
|
.description(tr(`thêm nhà model: ${Object.keys(PROVIDERS).join(", ")}`, `add a provider: ${Object.keys(PROVIDERS).join(", ")}`))
|
|
69
|
-
.option("--key-from-stdin", tr("đọc
|
|
70
|
-
.option("--name <model>", tr("model dùng khi chỉ ghi tên nhà, vd gpt → --name <
|
|
69
|
+
.option("--key-from-stdin", tr("đọc API key từ stdin (không bao giờ đưa khoá lên dòng lệnh)", "read the API key from stdin (never put keys on the command line)"))
|
|
70
|
+
.option("--name <model>", tr("model dùng khi chỉ ghi tên nhà, vd gpt → --name <model>", "model used when only the provider is given"))
|
|
71
71
|
.option("--url <url>", tr("địa chỉ máy chủ (Ollama hoặc cổng tương thích)", "server address (Ollama or a compatible endpoint)"))
|
|
72
72
|
.action(async (provider, o) => {
|
|
73
73
|
if (!isProvider(provider))
|
|
@@ -78,7 +78,7 @@ export function registerModel(program) {
|
|
|
78
78
|
if (o.keyFromStdin)
|
|
79
79
|
key = await readAll();
|
|
80
80
|
else if (hasTty())
|
|
81
|
-
key = await readHidden(tr(`
|
|
81
|
+
key = await readHidden(tr(`API key ${info0.label}: `, `${info0.label} API key: `));
|
|
82
82
|
else
|
|
83
83
|
throw new CliError(tr("Không có terminal: dùng --key-from-stdin.", "No terminal: use --key-from-stdin."), Exit.usage);
|
|
84
84
|
if (!key)
|
package/dist/commands/tools.js
CHANGED
|
@@ -160,7 +160,7 @@ export function registerTools(program) {
|
|
|
160
160
|
if (!pub)
|
|
161
161
|
throw new CliError(tr("Không thấy công cụ công khai này.", "Public tool not found."), Exit.notFound);
|
|
162
162
|
if (pub.kind !== "read")
|
|
163
|
-
throw new CliError(tr("
|
|
163
|
+
throw new CliError(tr("API key chỉ gọi được công cụ đọc.", "API keys can only call read tools."), Exit.forbidden);
|
|
164
164
|
value = await post(`/ai/tools/${pub.module}/${pub.name}/read`, args);
|
|
165
165
|
}
|
|
166
166
|
else {
|
|
@@ -63,7 +63,7 @@ export function verifySignature(secret, body, headers, toleranceSec = 300) {
|
|
|
63
63
|
if (h["webhook-signature"].split(" ").some((s) => eq(s.replace(/^v1,/, ""), expected)))
|
|
64
64
|
return { ok: true, scheme: "webhook-signature" };
|
|
65
65
|
}
|
|
66
|
-
return { ok: false, reason: tr("
|
|
66
|
+
return { ok: false, reason: tr("signature không khớp", "signature mismatch") };
|
|
67
67
|
}
|
|
68
68
|
async function findEndpoint(id) {
|
|
69
69
|
const e = (await listEndpoints()).find((x) => x.id.startsWith(id) || x.url === id);
|
|
@@ -72,7 +72,7 @@ async function findEndpoint(id) {
|
|
|
72
72
|
return e;
|
|
73
73
|
}
|
|
74
74
|
export function registerWebhooks(program) {
|
|
75
|
-
const wh = program.command("webhooks").alias("webhook").description(tr("sự kiện webhook: xem,
|
|
75
|
+
const wh = program.command("webhooks").alias("webhook").description(tr("sự kiện webhook: xem, trigger, nghe về máy", "webhook events: browse, trigger, listen locally"));
|
|
76
76
|
wh.command("events").description(tr("danh sách sự kiện", "list events")).option("--module <m>")
|
|
77
77
|
.action(async (o) => {
|
|
78
78
|
let list = await eventTypes();
|
|
@@ -164,7 +164,7 @@ export function registerWebhooks(program) {
|
|
|
164
164
|
.option("--limit <n>", "", "30")
|
|
165
165
|
.action(async (o) => {
|
|
166
166
|
const list = await listDeliveries({ endpoint: o.endpoint, status: o.status, limit: Number(o.limit) });
|
|
167
|
-
emit(list, () => info(table(["ID", tr("Sự kiện", "Event"), tr("Kết quả", "Result"), "HTTP", "ms", tr("
|
|
167
|
+
emit(list, () => info(table(["ID", tr("Sự kiện", "Event"), tr("Kết quả", "Result"), "HTTP", "ms", tr("Retry", "Retries"), tr("Lúc", "At")], list.map((d) => [
|
|
168
168
|
d.id.slice(0, 8), d.eventType, d.status === "success" ? c.green(d.status) : d.status === "failed" ? c.red(d.status) : d.status, d.responseStatus ?? "", d.durationMs ?? "", `${d.retryCount}/${d.maxRetries}`, when(d.createdAt),
|
|
169
169
|
]))));
|
|
170
170
|
});
|
|
@@ -175,11 +175,11 @@ export function registerWebhooks(program) {
|
|
|
175
175
|
const r = await post(`/webhook/deliveries/${full}/retry`, {});
|
|
176
176
|
emit(r, () => ok(tr("Đã xếp gửi lại.", "Retry queued.")));
|
|
177
177
|
});
|
|
178
|
-
wh.command("verify").description(tr("kiểm
|
|
178
|
+
wh.command("verify").description(tr("kiểm signature webhook tại máy (không gọi máy chủ)", "verify a webhook signature locally (no server call)"))
|
|
179
179
|
.requiredOption("--secret <secret>")
|
|
180
180
|
.requiredOption("--payload <file>", tr("thân thô đã nhận (- = stdin)", "raw received body (- = stdin)"))
|
|
181
181
|
.option("-H, --header <K: V>", tr("header đã nhận (lặp được)", "received header (repeatable)"), (v, p = []) => [...p, v])
|
|
182
|
-
.option("--signature <sig>", tr("
|
|
182
|
+
.option("--signature <sig>", tr("signature trong header *-Signature-256", "signature from the *-Signature-256 header"))
|
|
183
183
|
.option("--timestamp <ts>", tr("dấu thời gian trong header *-Timestamp", "timestamp from the *-Timestamp header"))
|
|
184
184
|
.action((o) => {
|
|
185
185
|
const body = readFileSync(o.payload === "-" ? 0 : o.payload, "utf8");
|
|
@@ -194,7 +194,7 @@ export function registerWebhooks(program) {
|
|
|
194
194
|
if (o.timestamp)
|
|
195
195
|
headers[tsHeader()] = o.timestamp;
|
|
196
196
|
const r = verifySignature(o.secret, body, headers);
|
|
197
|
-
emit(r, () => (r.ok ? ok(tr(`
|
|
197
|
+
emit(r, () => (r.ok ? ok(tr(`Signature hợp lệ (${r.scheme})`, `Valid signature (${r.scheme})`)) : warn(tr(`Không hợp lệ: ${r.reason}`, `Invalid: ${r.reason}`))));
|
|
198
198
|
if (!r.ok)
|
|
199
199
|
process.exitCode = Exit.general;
|
|
200
200
|
});
|
|
@@ -231,7 +231,7 @@ async function listen(o) {
|
|
|
231
231
|
catch (e) {
|
|
232
232
|
if (stop.signal.aborted)
|
|
233
233
|
break;
|
|
234
|
-
warn(tr(`Mất kết nối,
|
|
234
|
+
warn(tr(`Mất kết nối, nối lại sau ${backoff / 1000}s…`, `Disconnected, retrying in ${backoff / 1000}s…`));
|
|
235
235
|
await new Promise((r) => setTimeout(r, backoff));
|
|
236
236
|
backoff = Math.min(backoff * 2, 30_000);
|
|
237
237
|
void e;
|
package/dist/core/i18n.js
CHANGED
|
@@ -25,22 +25,22 @@ export function currentLang() {
|
|
|
25
25
|
const vi = {
|
|
26
26
|
"auth.notLoggedIn": "Chưa đăng nhập.",
|
|
27
27
|
"auth.notLoggedIn.hint": "Chạy `{bin} login` (hoặc đặt {prefix}_API_KEY cho CI).",
|
|
28
|
-
"auth.expired": "
|
|
28
|
+
"auth.expired": "Session trên máy này đã hết.",
|
|
29
29
|
"auth.expired.hint": "Đăng nhập lại: `{bin} login`",
|
|
30
|
-
"auth.revoked": "
|
|
30
|
+
"auth.revoked": "Session trên máy này đã bị đăng xuất (từ web hoặc quản trị).",
|
|
31
31
|
"auth.clock": "Đồng hồ máy lệch {min} phút so với máy chủ, token bị coi là sai.",
|
|
32
|
-
"auth.clock.hint": "Bật đồng bộ giờ rồi
|
|
33
|
-
"auth.apiKeyNoChat": "
|
|
32
|
+
"auth.clock.hint": "Bật đồng bộ giờ rồi chạy lại (`{bin} doctor`).",
|
|
33
|
+
"auth.apiKeyNoChat": "API key không dùng được chế độ chat AI (đó là danh tính agent, không phải người).",
|
|
34
34
|
"auth.apiKeyNoChat.hint": "Dùng `{bin} tools call …`, hoặc `{bin} login --ci` trong CI.",
|
|
35
35
|
"net.failed": "Không kết nối được tới {host}.",
|
|
36
36
|
"net.failed.hint": "Kiểm mạng, proxy (HTTPS_PROXY) hoặc chạy `{bin} doctor`.",
|
|
37
37
|
"net.tls": "Không tin được chứng chỉ của máy chủ (có thể do proxy công ty).",
|
|
38
|
-
"net.tls.hint": "Đặt NODE_EXTRA_CA_CERTS=<
|
|
38
|
+
"net.tls.hint": "Đặt NODE_EXTRA_CA_CERTS=<ca-file> hoặc HTTPS_PROXY=…",
|
|
39
39
|
"http.forbidden": "Không đủ quyền.",
|
|
40
40
|
"http.notFound": "Không tìm thấy.",
|
|
41
|
-
"http.rateLimited": "Hỏi nhanh quá,
|
|
41
|
+
"http.rateLimited": "Hỏi nhanh quá, đợi {sec} giây rồi hỏi tiếp.",
|
|
42
42
|
"http.server": "{name} đang lỗi (mã yêu cầu {rid}).",
|
|
43
|
-
"http.server.hint": "
|
|
43
|
+
"http.server.hint": "Chạy lại sau ít phút, hoặc `/support` kèm mã này.",
|
|
44
44
|
"http.tooOld": "Bản {cur} đã cũ, máy chủ cần ≥ {min}.",
|
|
45
45
|
"http.tooOld.hint": "Chạy `{bin} update`.",
|
|
46
46
|
"ws.gone": "Bạn không còn ở workspace này, hoặc workspace đang tạm khoá.",
|
|
@@ -63,7 +63,7 @@ const en = {
|
|
|
63
63
|
"net.failed": "Cannot reach {host}.",
|
|
64
64
|
"net.failed.hint": "Check your network, proxy (HTTPS_PROXY) or run `{bin} doctor`.",
|
|
65
65
|
"net.tls": "The server certificate is not trusted (often a corporate proxy).",
|
|
66
|
-
"net.tls.hint": "Set NODE_EXTRA_CA_CERTS=<ca
|
|
66
|
+
"net.tls.hint": "Set NODE_EXTRA_CA_CERTS=<ca-file> or HTTPS_PROXY=…",
|
|
67
67
|
"http.forbidden": "Permission denied.",
|
|
68
68
|
"http.notFound": "Not found.",
|
|
69
69
|
"http.rateLimited": "Too many requests, retry in {sec} seconds.",
|
package/dist/program.js
CHANGED
|
@@ -31,12 +31,12 @@ export function buildProgram() {
|
|
|
31
31
|
.option("-r, --resume [id]", tr("mở lại hội thoại cũ (không id = chọn trong danh sách)", "resume a conversation (no id = pick from a list)"))
|
|
32
32
|
.option("-f, --file <path>", tr("đính kèm tệp Excel/CSV, ảnh hoặc tệp chữ (lặp được, tối đa 3)", "attach an Excel/CSV, image or text file (repeatable, max 3)"), collect)
|
|
33
33
|
.addOption(new Option("--profile <name>", tr("hồ sơ đăng nhập", "login profile")))
|
|
34
|
-
.option("--sandbox", tr("dùng
|
|
34
|
+
.option("--sandbox", tr("dùng sandbox", "use the sandbox environment"))
|
|
35
35
|
.option("-w, --workspace <code>", tr("chạy lệnh này trên workspace khác", "run this command on another workspace"))
|
|
36
36
|
.option("--agent <code>", tr("chọn Agent thay cho Trợ lý chung", "choose an Agent instead of the general assistant"))
|
|
37
|
-
.option("--model <provider/name>", tr("dùng model của bạn qua MCP: claude, gemini, gpt, grok, ollama/<
|
|
38
|
-
.addOption(new Option("--mode <mode>", tr("
|
|
39
|
-
.addOption(new Option("--output-format <format>", tr("
|
|
37
|
+
.option("--model <provider/name>", tr("dùng model của bạn qua MCP: claude, gemini, gpt, grok, ollama/<name>", "use your own model over MCP: claude, gemini, gpt, grok, ollama/<name>"))
|
|
38
|
+
.addOption(new Option("--mode <mode>", tr("permission mode", "permission mode")).choices(["read", "propose", "auto"]))
|
|
39
|
+
.addOption(new Option("--output-format <format>", tr("output format", "output format")).choices(["text", "json", "stream-json"]))
|
|
40
40
|
.option("--json", tr("viết tắt của --output-format json", "shorthand for --output-format json"))
|
|
41
41
|
.addOption(new Option("--lang <lang>", tr("ngôn ngữ", "language")).choices(["vi", "en"]))
|
|
42
42
|
.option("--no-color", tr("tắt màu", "disable colors"))
|
package/dist/repl/App.js
CHANGED
|
@@ -286,7 +286,7 @@ export function App(props) {
|
|
|
286
286
|
if (out.proposedAction)
|
|
287
287
|
await handleProposal(out.proposedAction);
|
|
288
288
|
if (out.ended === "escalate")
|
|
289
|
-
say(c.dim(tr(" Cần người hỗ trợ? /support <
|
|
289
|
+
say(c.dim(tr(" Cần người hỗ trợ? /support <problem>", " Need a human? /support <problem>")));
|
|
290
290
|
refreshUsage();
|
|
291
291
|
}
|
|
292
292
|
catch (e) {
|
|
@@ -422,7 +422,7 @@ export function App(props) {
|
|
|
422
422
|
` ${tr("Đang dùng", "Using")} ${c.bold(local ? local.model.label : b.aiName)}`,
|
|
423
423
|
` ${c.cyan(b.bin.padEnd(10))} ${b.aiName}`,
|
|
424
424
|
...rows.map((r) => ` ${c.cyan(r.name.padEnd(10))} ${r.entry.model ?? ""}${r.hasKey ? "" : c.yellow(tr(" (chưa có khoá)", " (no key)"))}`),
|
|
425
|
-
c.dim(` /model ${b.bin} · /model claude · /model ollama/<model> · ${b.bin} model add <
|
|
425
|
+
c.dim(` /model ${b.bin} · /model claude · /model ollama/<model> · ${b.bin} model add <provider>`),
|
|
426
426
|
].join("\n"));
|
|
427
427
|
break;
|
|
428
428
|
}
|
|
@@ -461,7 +461,7 @@ export function App(props) {
|
|
|
461
461
|
case "why": {
|
|
462
462
|
const id = arg || lastOutcome?.runId;
|
|
463
463
|
if (!id)
|
|
464
|
-
throw new Error(tr("Chưa có câu trả lời nào trong
|
|
464
|
+
throw new Error(tr("Chưa có câu trả lời nào trong session này.", "No answer in this session yet."));
|
|
465
465
|
const run = await get(`/ai/runs/${id}`);
|
|
466
466
|
say(runLines(run).join("\n"));
|
|
467
467
|
break;
|
|
@@ -503,7 +503,7 @@ export function App(props) {
|
|
|
503
503
|
const by = new Map();
|
|
504
504
|
for (const t of list)
|
|
505
505
|
by.set(t.module, (by.get(t.module) ?? 0) + 1);
|
|
506
|
-
say([...by.entries()].map(([m, n]) => ` ${m.padEnd(12)} ${n}`).join("\n") + c.dim(tr(`\n ${b.bin} tools list · ${b.bin} tools describe <
|
|
506
|
+
say([...by.entries()].map(([m, n]) => ` ${m.padEnd(12)} ${n}`).join("\n") + c.dim(tr(`\n ${b.bin} tools list · ${b.bin} tools describe <name>`, `\n ${b.bin} tools list · ${b.bin} tools describe <name>`)));
|
|
507
507
|
break;
|
|
508
508
|
}
|
|
509
509
|
case "usage": {
|
|
@@ -521,7 +521,7 @@ export function App(props) {
|
|
|
521
521
|
}
|
|
522
522
|
case "support": {
|
|
523
523
|
if (!arg)
|
|
524
|
-
throw new Error(tr("Ghi rõ vấn đề: /support <
|
|
524
|
+
throw new Error(tr("Ghi rõ vấn đề: /support <problem>", "Describe it: /support <problem>"));
|
|
525
525
|
const r = await escalate(sessionId, arg);
|
|
526
526
|
say(`${c.green("✓")} ${tr(`Đã chuyển cho nhân viên (${r.status}) · chờ khoảng ${r.estimatedWaitMinutes} phút`, `Handed over (${r.status}) · about ${r.estimatedWaitMinutes} min`)}`);
|
|
527
527
|
break;
|
package/dist/repl/slash.js
CHANGED
|
@@ -5,29 +5,29 @@ const notes = () => `${brand().bin.toUpperCase()}.md`;
|
|
|
5
5
|
export const SLASH = [
|
|
6
6
|
{ name: "help", description: () => tr("Danh sách lệnh và phím tắt", "Commands and shortcuts") },
|
|
7
7
|
{ name: "new", description: () => tr("Hội thoại mới (giữ workspace, agent)", "New conversation (keeps workspace, agent)") },
|
|
8
|
-
{ name: "resume", args: "[
|
|
9
|
-
{ name: "rename", args: "<
|
|
8
|
+
{ name: "resume", args: "[query]", description: () => tr("Mở lại hội thoại cũ", "Resume an earlier conversation") },
|
|
9
|
+
{ name: "rename", args: "<name>", description: () => tr("Đặt tên hội thoại này", "Rename this conversation") },
|
|
10
10
|
{ name: "pin", description: () => tr("Ghim / bỏ ghim hội thoại", "Pin / unpin this conversation") },
|
|
11
|
-
{ name: "workspace", args: "[
|
|
12
|
-
{ name: "agent", args: "[
|
|
13
|
-
{ name: "mode", args: "read|propose|auto", description: () => tr("
|
|
14
|
-
{ name: "model", args: "[
|
|
15
|
-
{ name: "attach", args: "<
|
|
16
|
-
{ name: "pending", description: () => tr("
|
|
11
|
+
{ name: "workspace", args: "[code]", description: () => tr("Đổi workspace đang làm việc", "Switch workspace") },
|
|
12
|
+
{ name: "agent", args: "[code]", description: () => tr("Chọn Agent: Trợ lý chung, Bán hàng, CSKH…", "Choose an Agent: general, sales, support…"), platforms: ["nextx"] },
|
|
13
|
+
{ name: "mode", args: "read|propose|auto", description: () => tr("Permission mode: chỉ đọc · đề xuất · tự ghi việc thường", "Permission mode: read · propose · auto") },
|
|
14
|
+
{ name: "model", args: "[name]", description: () => tr(`Đổi model: ${brand().aiName} hoặc model của bạn`, `Switch model: ${brand().aiName} or your own`) },
|
|
15
|
+
{ name: "attach", args: "<file>", description: () => tr("Đính kèm tệp Excel / CSV / ảnh / tệp chữ", "Attach an Excel / CSV / image / text file") },
|
|
16
|
+
{ name: "pending", description: () => tr("Write proposal đang chờ xác nhận", "Writes waiting for confirmation"), platforms: ["nextx"] },
|
|
17
17
|
{ name: "undo", args: "[id]", description: () => tr("Hoàn tác lần ghi gần nhất", "Undo the last write") },
|
|
18
18
|
{ name: "journal", description: () => tr("Nhật ký những gì AI đã ghi", "Journal of what the AI wrote"), platforms: ["nextx"] },
|
|
19
19
|
{ name: "why", args: "[runId]", description: () => tr("Vì sao AI trả lời vậy (các bước, công cụ đã gọi)", "Why the AI answered that way (steps, tools)") },
|
|
20
20
|
{ name: "memory", description: () => tr("AI đang nhớ gì về công ty — sửa, khoá, quên", "What the AI remembers — edit, lock, forget") },
|
|
21
|
-
{ name: "schedule", args: '"
|
|
21
|
+
{ name: "schedule", args: '"question" daily 08:00', description: () => tr("Lên lịch một câu hỏi chạy hằng ngày / tuần / tháng", "Schedule a question daily / weekly / monthly") },
|
|
22
22
|
{ name: "library", description: () => tr("Ảnh, tài liệu, tệp AI đã tạo", "Images, documents and files the AI produced"), platforms: ["nextx"] },
|
|
23
|
-
{ name: "export", args: "md|json [
|
|
23
|
+
{ name: "export", args: "md|json [file]", description: () => tr("Xuất hội thoại ra Markdown / JSON", "Export the conversation to Markdown / JSON") },
|
|
24
24
|
{ name: "tools", description: () => tr("Công cụ AI bạn đang được dùng (theo quyền thật)", "AI tools you can use (by your real permissions)") },
|
|
25
25
|
{ name: "usage", description: () => brand().platform === "nextx" ? tr("Lượt AI tháng này", "AI turns this month") : tr("Câu hỏi AI còn lại tuần này", "AI questions left this week") },
|
|
26
26
|
{ name: "feedback", args: "up|down", description: () => tr("Báo câu trả lời vừa rồi tốt / chưa tốt", "Rate the last answer") },
|
|
27
|
-
{ name: "support", args: "<
|
|
27
|
+
{ name: "support", args: "<problem>", description: () => tr("Chuyển cho nhân viên hỗ trợ (mở ticket)", "Hand over to a support agent (ticket)"), platforms: ["nextx"] },
|
|
28
28
|
{ name: "status", description: () => tr("Tài khoản, workspace, môi trường, phiên bản", "Account, workspace, environment, version") },
|
|
29
29
|
{ name: "doctor", description: () => tr("Kiểm kết nối, đồng hồ, token, gói", "Check connectivity, clock, token, plan") },
|
|
30
|
-
{ name: "config", args: "[
|
|
30
|
+
{ name: "config", args: "[key value]", description: () => tr("Cài đặt: ngôn ngữ, màu, thông báo cập nhật", "Settings: language, color, update notices") },
|
|
31
31
|
{ name: "init", description: () => tr(`Tạo ${notes()}: chỉ dẫn riêng cho thư mục này`, `Create ${notes()}: instructions for this folder`) },
|
|
32
32
|
{ name: "mcp", description: () => tr("Cài máy chủ MCP vào client khác", "Install the MCP server into another client") },
|
|
33
33
|
{ name: "logout", description: () => tr("Đăng xuất hồ sơ này", "Log out this profile") },
|
|
@@ -39,7 +39,7 @@ export const SHORTCUTS = [
|
|
|
39
39
|
{ keys: "/", description: () => tr("Menu lệnh", "Command menu") },
|
|
40
40
|
{ keys: "@", description: () => tr("Đính kèm tệp, chọn Agent cho câu này", "Attach a file, pick an Agent for this message") },
|
|
41
41
|
{ keys: "#", description: () => tr(`Ghi nhớ nhanh vào ${notes()} của thư mục`, `Quick note into this folder's ${notes()}`) },
|
|
42
|
-
{ keys: "Shift+Tab", description: () => tr("Xoay
|
|
42
|
+
{ keys: "Shift+Tab", description: () => tr("Xoay permission mode", "Cycle permission mode") },
|
|
43
43
|
{ keys: "Esc", description: () => tr("Dừng câu trả lời đang chạy", "Stop the running answer") },
|
|
44
44
|
{ keys: "Esc Esc", description: () => tr("Sửa lại câu vừa gửi", "Edit the last message") },
|
|
45
45
|
{ keys: "↑ / ↓", description: () => tr("Lịch sử câu đã gõ", "Input history") },
|