@nextxai/cli 0.3.3 → 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 CHANGED
@@ -1,5 +1,14 @@
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
+
7
+ ## 0.3.4
8
+
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.
10
+ - Security reports go through GitHub's private vulnerability reporting on this repository.
11
+
3
12
  ## 0.3.3
4
13
 
5
14
  - Maintenance release: tidier source comments, no change in behaviour.
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 giới hạn tốc độ hoặc hết hạn mức.`, `${who}: the provider is rate limiting or out of quota.`) + (message ? ` (${message})` : ""), Exit.quota);
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) {
@@ -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 phạm vi duyệt trước).", "Written (CI, within the pre-approved scope)."));
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ử, đề xuất ghi, bộ nhớ, lịch chạy", "AI assistant: ask, history, proposals, memory, schedules"));
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ó phạm vi duyệt trước).", "Applying needs an interactive terminal (or a CI login with a pre-approved scope)."), Exit.notConfirmed);
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) {
@@ -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ả mã trạng thái và header", "print status and headers too"))
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();
@@ -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("Phiên", "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")}`);
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 khoá API: ${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`));
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 phiên trên máy chủ)", "log out this profile (revokes the server session)"))
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á phiên trên máy (${name}), nhưng không tới được máy chủ: phiên 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`));
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, phiên", "account, workspace, environment, AI plan, session"))
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 khoá API từ ${b.envPrefix}_API_KEY (danh tính agent).`, `Using the API key from ${b.envPrefix}_API_KEY (agent identity).`)));
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("phiên", "session")} (${s?.via}) · ${storageKind() === "keychain" ? "Keychain" : tr("tệp", "file")}`}`);
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("phiên đăng nhập của bạn trên mọi thiết bị", "your login sessions on all devices"));
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 phiên", "list sessions"))
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 phiên, hoặc --others để thu hồi mọi phiên khác", "revoke one session, or --others for all other sessions"))
153
- .option("--others", tr("mọi phiên trừ phiên này", "every session except this one"))
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 phiên khác.", "Revoked all other sessions."));
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 phiên (xem `sessions list`) hoặc --others.", "Pass a session id (see `sessions list`) or --others."), Exit.usage);
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 phiên này.", "Session not found."), Exit.notFound);
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 phiên ${hit.id.slice(0, 8)}.`, `Revoked session ${hit.id.slice(0, 8)}.`));
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ý khoá API 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("khai báo CI (xem: ci trust list)", "CI trust (see: ci trust list)"))
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ý khoá API)", "approve now (needs API-key manage permission)"))
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));
@@ -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 phạm vi thì để lại đề xuất chờ người duyệt (mã thoát 7).", "Writes only within an approved policy (ci policy request); anything else stays a proposal for a person (exit code 7).")}
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 bí mật (OIDC)", "secretless CI login (OIDC)"));
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 khoá API: ${brand().envPrefix}_API_KEY`, `use an API key: ${brand().envPrefix}_API_KEY`));
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 <mã>`, `\nLet CI write within a ceiling: ${brand().bin} ci policy request --trust ${t.repository} --kinds <code>`)));
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
  });
@@ -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("khoá API của workspace", "workspace API keys"));
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 bí mật của CI, trình quản lý mật khẩu).", "Shown only once. Store it now (CI secret, password manager)."));
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
  });
@@ -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 khoá API 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 <tên model>", "model used when only the provider is given"))
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(`Khoá API ${info0.label}: `, `${info0.label} API key: `));
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)
@@ -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("Khoá API chỉ gọi được công cụ đọc.", "API keys can only call read tools."), Exit.forbidden);
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("chữ ký không khớp", "signature mismatch") };
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, bắn thử, nghe về máy", "webhook events: browse, trigger, listen locally"));
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("Thử lại", "Retries"), tr("Lúc", "At")], list.map((d) => [
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 chữ ký webhook tại máy (không gọi máy chủ)", "verify a webhook signature locally (no server call)"))
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("chữ ký trong header *-Signature-256", "signature from the *-Signature-256 header"))
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(`Chữ ký hợp lệ (${r.scheme})`, `Valid signature (${r.scheme})`)) : warn(tr(`Không hợp lệ: ${r.reason}`, `Invalid: ${r.reason}`))));
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, thử lại sau ${backoff / 1000}s…`, `Disconnected, retrying in ${backoff / 1000}s…`));
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": "Phiên trên máy này đã hết.",
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": "Phiên trên máy này đã bị đăng xuất (từ web hoặc quản trị).",
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 thử lại (`{bin} doctor`).",
33
- "auth.apiKeyNoChat": "Khoá API không dùng được chế độ chat AI (đó là danh tính agent, không phải người).",
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=<tệp CA> hoặc HTTPS_PROXY=…",
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á, thử lại sau {sec} giây.",
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": "Thử lại sau, hoặc `/support` kèm mã này.",
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 file> or HTTPS_PROXY=…",
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 môi trường thử nghiệm", "use the sandbox environment"))
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/<tên>", "use your own model over MCP: claude, gemini, gpt, grok, ollama/<name>"))
38
- .addOption(new Option("--mode <mode>", tr("chế độ quyền", "permission mode")).choices(["read", "propose", "auto"]))
39
- .addOption(new Option("--output-format <format>", tr("định dạng đầu ra", "output format")).choices(["text", "json", "stream-json"]))
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 <vấn đề>", " Need a human? /support <problem>")));
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 <nhà>`),
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 phiên này.", "No answer in this session yet."));
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 <tên>`, `\n ${b.bin} tools list · ${b.bin} tools describe <name>`)));
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 <vấn đề>", "Describe it: /support <problem>"));
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;
@@ -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: "[từ khoá]", description: () => tr("Mở lại hội thoại cũ", "Resume an earlier conversation") },
9
- { name: "rename", args: "<tên>", description: () => tr("Đặt tên hội thoại này", "Rename this conversation") },
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: "[mã]", description: () => tr("Đổi workspace đang làm việc", "Switch workspace") },
12
- { name: "agent", args: "[mã]", 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("Chế độ quyền: chỉ đọc · đề xuất · tự ghi việc thường", "Permission mode: read · propose · auto") },
14
- { name: "model", args: "[tên]", description: () => tr(`Đổi model: ${brand().aiName} hoặc model của bạn`, `Switch model: ${brand().aiName} or your own`) },
15
- { name: "attach", args: "<tệp>", 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("Đề xuất ghi đang chờ xác nhận", "Writes waiting for confirmation"), platforms: ["nextx"] },
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: '"câu" 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") },
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 [tệp]", description: () => tr("Xuất hội thoại ra Markdown / JSON", "Export the conversation to Markdown / 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: "<vấn đề>", description: () => tr("Chuyển cho nhân viên hỗ trợ (mở ticket)", "Hand over to a support agent (ticket)"), platforms: ["nextx"] },
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: "[khoá giá_trị]", description: () => tr("Cài đặt: ngôn ngữ, màu, thông báo cập nhật", "Settings: language, color, update notices") },
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 chế độ quyền", "Cycle permission mode") },
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") },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextxai/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "NextX in your terminal: chat with NextX AI on your workspace data, call the API, manage keys and webhooks",
5
5
  "keywords": [
6
6
  "nextx",