@kesflow/kf 0.1.11 → 0.1.12

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/SKILL.md CHANGED
@@ -16,13 +16,13 @@ description: Thao tác nội bộ KesFlow qua CLI `kf` thay cho bấm màn ops,
16
16
  ## Cách dùng
17
17
  - Danh sách thao tác (lấy trực tiếp từ server, luôn đúng): `kf --help`. Tham số một thao tác: `kf <op> --help`, `kf schema <op>`.
18
18
  - Gọi: `kf <op> --<tham-so> <giá trị>` hoặc `kf <op> --json '{...}'`. Kết quả: một dòng JSON (`--pretty` để đọc).
19
- - Không chắc thì chạy thử trước: thêm `--dry-run` (chỉ kiểm, không ghi gì).
19
+ - Không chắc thì chạy thử trước: thêm `--dry-run`: chỉ kiểm khuôn tham số, không kiểm quyền hay điều kiện nghiệp vụ, không ghi gì. Thao tác `[high]` chạy thử không cần `--yes`.
20
20
  - Tải tệp từ máy lên buổi học: `kf lms upload <tệp> --buoi-id <id> [--loai slide_pdf|slide_html|record|video_xem_truoc] [--ten ...]`.
21
21
  - Tải tệp tài nguyên lên sản phẩm: `kf tn upload <tệp> --ma <SP>` (tối đa 256 MiB, gửi tới `https://ops.kesflow.vn`; dùng `KF_NB_URL` cho host nội bộ thử/cục bộ).
22
22
  - Tìm `buoi-id`: `kf status` (lớp tôi phụ trách) → `kf lop-chi-tiet --ma <MÃ LỚP>`.
23
23
 
24
24
  ## Thao tác rủi ro cao (`[high]`, cần `--yes`)
25
- - Ví dụ `lms-dang-buoi --gui-thu true` gửi thư cho **cả lớp thật**. `lms-dang-buoi` luôn cần `--yes`, kể cả khi không gửi thư.
25
+ - Ví dụ `lms-dang-buoi --gui-thu true` gửi thư cho **cả lớp thật**. `lms-dang-buoi` luôn cần `--yes` (trừ `--dry-run`), kể cả khi không gửi thư.
26
26
  - Chỉ thêm `--yes` **sau khi người dùng đồng ý rõ trong chat** với đúng nội dung: buổi nào, lớp nào, có gửi thư không. Không tự suy ra ý định gửi thư.
27
27
  - `--yes` không thay người duyệt; người dùng nên để Claude Code hỏi quyền trước lệnh `kf … --yes`.
28
28
  - `sp-gia`/`sp-trang-thai` đổi giá và mở bán thật: chỉ dùng `--yes` sau khi người dùng nêu đúng mã sản phẩm và con số trong chat.
@@ -75,7 +75,7 @@ description: Thao tác nội bộ KesFlow qua CLI `kf` thay cho bấm màn ops,
75
75
 
76
76
  ## Xoá dữ liệu (chỉ CEO — cực kỳ nguy hiểm, không hoàn tác)
77
77
  - Luôn `kf xoa-xem --loai <san_pham|voucher|ghi_danh> --khoa <mã/id>` trước. Trình cho người dùng **nguyên văn**: từng loại dữ liệu và số dòng sẽ mất, các tín hiệu "dữ liệu thật", số dòng Base Tiền phải xoá tay, và câu "không hoàn tác được". Bị chặn (`chan`) thì dừng, báo lý do.
78
- - Dữ liệu rác: một lệnh `kf xoa … --yes` sau khi người dùng đồng ý. Dữ liệu thật: lệnh đầu trả `NB_XOA_CAN_XAC_NHAN_2` kèm mã — **dừng, hỏi người dùng lần hai**, chỉ khi họ đồng ý mới gọi lại với mã đó. Không bao giờ gửi hai xác nhận trong một lượt.
78
+ - Dữ liệu rác: một lệnh `kf xoa … --yes` sau khi người dùng đồng ý. Dữ liệu thật: lệnh đầu trả `NB_XOA_CAN_XAC_NHAN_2` kèm mã ở trường `ly_do` — **dừng, hỏi người dùng lần hai**, chỉ khi họ đồng ý mới gọi lại với `--xac-nhan-2 <mã đó>`. Không bao giờ gửi hai xác nhận trong một lượt.
79
79
  - `NB_XOA_DA_DOI`: dữ liệu đã đổi từ lúc xem — xem lại từ đầu. `NB_XOA_CHAN`: bị chặn (vd sổ chi trả affiliate, SP quản lý ở Base Tiền) — dừng, báo lý do. Giao dịch ngân hàng của đơn bị xoá không mất, quay về "Chờ xử lý".
80
80
 
81
81
  ## Hoàn tiền và hoá đơn (CEO, kế toán; admin đọc trong phạm vi)
package/dist/api.d.ts CHANGED
@@ -2,7 +2,8 @@ export declare class CliError extends Error {
2
2
  readonly exitCode: number;
3
3
  readonly code: string;
4
4
  readonly http: number | null;
5
- constructor(message: string, exitCode: number, code: string, http?: number | null);
5
+ readonly detail: Record<string, unknown>;
6
+ constructor(message: string, exitCode: number, code: string, http?: number | null, detail?: Record<string, unknown>);
6
7
  }
7
8
  export declare function httpExit(status: number): number;
8
9
  export declare function request(baseUrl: string, token: string, path: string, body?: Record<string, unknown>): Promise<unknown>;
package/dist/api.js CHANGED
@@ -2,11 +2,13 @@ export class CliError extends Error {
2
2
  exitCode;
3
3
  code;
4
4
  http;
5
- constructor(message, exitCode, code, http = null) {
5
+ detail;
6
+ constructor(message, exitCode, code, http = null, detail = {}) {
6
7
  super(message);
7
8
  this.exitCode = exitCode;
8
9
  this.code = code;
9
10
  this.http = http;
11
+ this.detail = detail;
10
12
  }
11
13
  }
12
14
  export function httpExit(status) {
@@ -44,7 +46,15 @@ export async function request(baseUrl, token, path, body) {
44
46
  const obj = data && typeof data === 'object' ? data : {};
45
47
  const candidate = typeof obj.ma === 'string' ? obj.ma : typeof obj.loai === 'string' ? obj.loai : '';
46
48
  const code = /^[A-Za-z][A-Za-z0-9_]{0,79}$/.test(candidate) ? candidate : `HTTP_${response.status}`;
47
- throw new CliError(code, httpExit(response.status), code, response.status);
49
+ // Server error envelope (thanLoiAgent) carries what the agent must act on next, e.g. the second-confirmation
50
+ // code of NB_XOA_CAN_XAC_NHAN_2 in ly_do. Pass through only these keys, as strings or string lists.
51
+ const detail = {};
52
+ for (const key of ['ly_do', 'chi_tiet', 'khoa', 'ket_qua_khop']) {
53
+ const value = obj[key];
54
+ if (typeof value === 'string' || (Array.isArray(value) && value.every((x) => typeof x === 'string')))
55
+ detail[key] = value;
56
+ }
57
+ throw new CliError(code, httpExit(response.status), code, response.status, detail);
48
58
  }
49
59
  return data;
50
60
  }
package/dist/cli.js CHANGED
@@ -253,6 +253,6 @@ stdout.on('error', (error) => { if (error.code === 'EPIPE')
253
253
  process.exit(0); throw error; });
254
254
  run(process.argv.slice(2)).catch((error) => {
255
255
  const e = error instanceof CliError ? error : new CliError('Lỗi hệ thống', 5, 'INTERNAL');
256
- stderr.write(`${redact(JSON.stringify({ error: e.message, code: e.code, http: e.http }))}\n`);
256
+ stderr.write(`${redact(JSON.stringify({ error: e.message, code: e.code, http: e.http, ...e.detail }))}\n`);
257
257
  process.exitCode = e.exitCode;
258
258
  });
package/dist/ops.js CHANGED
@@ -83,9 +83,11 @@ export function parseBody(op, args) {
83
83
  throw new CliError(`Thiếu giá trị: --${flag}`, 2, 'USAGE');
84
84
  body[key] = scalar(props[key], raw);
85
85
  }
86
- if (op.risk === 'high' && !yes)
86
+ // --dry-run needs no --yes: the server parses the body and returns before running the op, in a rolled-back
87
+ // transaction (apps/web/lib/agent/http.ts). xac_nhan is still set so the dry run checks the same body.
88
+ if (op.risk === 'high' && !yes && !dryRun)
87
89
  throw new CliError('Thao tác rủi ro cao cần --yes', 2, 'USAGE');
88
- if (yes && op.risk === 'high')
90
+ if ((yes || dryRun) && op.risk === 'high')
89
91
  body.xac_nhan = true;
90
92
  if (dryRun)
91
93
  body.dry_run = true;
@@ -97,5 +99,5 @@ export function operationHelp(op) {
97
99
  const type = schema.type || schema.anyOf?.map((s) => s.type).filter(Boolean).join('|') || 'JSON';
98
100
  return ` --${name.replaceAll('_', '-')} <${type}>${required}`;
99
101
  }).join('\n');
100
- return `${op.id} [${op.risk}] — ${op.tom_tat}\nRoles: ${op.roles.join(', ')}\n${props}\n --json '<object>' --dry-run --pretty${op.risk === 'high' ? ' --yes (required)' : ''}`;
102
+ return `${op.id} [${op.risk}] — ${op.tom_tat}\nRoles: ${op.roles.join(', ')}\n${props}\n --json '<object>' --dry-run --pretty${op.risk === 'high' ? ' --yes (required, trừ khi --dry-run)' : ''}`;
101
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kesflow/kf",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "KesFlow agent CLI",
5
5
  "type": "module",
6
6
  "bin": {