@nalvietnam/avatar-cli 3.25.3 → 3.27.0
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 +6 -1
- package/dist/core/index.cjs +2 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.js +101 -94
- package/dist/index.js.map +1 -1
- package/dist/lib/compare-pack-semver-and-detect-downgrade.d.ts +2 -0
- package/dist/lib/pack-manifest-contract.d.ts +26 -0
- package/dist/lib/pick-latest-stable-semver-tag.d.ts +10 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -56,6 +56,11 @@ avatar status
|
|
|
56
56
|
# Chẩn đoán
|
|
57
57
|
avatar doctor
|
|
58
58
|
avatar doctor --fix
|
|
59
|
+
|
|
60
|
+
# Nâng cấp CLI + đồng bộ pack cho MỌI workspace đã đăng ký (không cần cd vào từng nơi)
|
|
61
|
+
avatar update # update CLI rồi chạy `sync --all`
|
|
62
|
+
avatar update --tag next # theo kênh beta
|
|
63
|
+
avatar update --no-sync # chỉ update CLI, không đụng workspace nào (script/CI)
|
|
59
64
|
```
|
|
60
65
|
|
|
61
66
|
## Trạng thái implement
|
|
@@ -75,7 +80,7 @@ avatar doctor --fix
|
|
|
75
80
|
| `ai` | ✅ implement thật (setup AI provider) |
|
|
76
81
|
| `comm-connect` | ✅ implement thật |
|
|
77
82
|
| `gitnexus` | ✅ implement thật (setup + analyze) |
|
|
78
|
-
| `update` | ✅ implement thật (self-update CLI qua `npm i -g`, hỗ trợ dist-tag) |
|
|
83
|
+
| `update` | ✅ implement thật (self-update CLI qua `npm i -g`, hỗ trợ dist-tag; xong thì re-exec binary MỚI chạy `sync --all` — opt-out `--no-sync`) |
|
|
79
84
|
| `uninstall` | ✅ implement thật |
|
|
80
85
|
| `print-id-token` | ✅ implement thật |
|
|
81
86
|
| `scan` | ⏳ stub (5 scanner cũng stub) |
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @nalvietnam/avatar-cli — built with tsup
|
|
2
|
+
"use strict";var c=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var N=(r,n)=>{for(var e in n)c(r,e,{get:n[e],enumerable:!0})},S=(r,n,e,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of V(n))!M.call(r,t)&&t!==e&&c(r,t,{get:()=>n[t],enumerable:!(i=h(n,t))||i.enumerable});return r};var x=r=>S(c({},"__esModule",{value:!0}),r);var T={};N(T,{PACK_MANIFEST_FILENAME:()=>P,PACK_VERSION_FILENAME:()=>g,classifyPackVersionTransition:()=>d,parsePackManifest:()=>k,parseSemVerTag:()=>u});module.exports=x(T);var y=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;function u(r){let n=r.match(y);if(!n)return null;let[,e,i,t,s]=n;return{raw:r,major:Number.parseInt(e??"0",10),minor:Number.parseInt(i??"0",10),patch:Number.parseInt(t??"0",10),prerelease:s??null}}function E(r,n){if(r===null&&n===null)return 0;if(r===null)return 1;if(n===null)return-1;let e=r.split("."),i=n.split("."),t=Math.max(e.length,i.length);for(let s=0;s<t;s++){let a=e[s],o=i[s];if(a===void 0)return-1;if(o===void 0)return 1;let l=/^\d+$/.test(a),p=/^\d+$/.test(o);if(l&&p){let f=Number(a)-Number(o);if(f!==0)return f<0?-1:1;continue}if(l!==p)return l?-1:1;if(a!==o)return a<o?-1:1}return 0}function j(r,n){return r.major!==n.major?r.major<n.major?-1:1:r.minor!==n.minor?r.minor<n.minor?-1:1:r.patch!==n.patch?r.patch<n.patch?-1:1:E(r.prerelease,n.prerelease)}function m(r){if(typeof r!="string")return null;let n=r.trim();return n===""?null:u(n)}function d(r,n){let e=m(r),i=m(n);if(e===null||i===null)return"unknown";let t=j(e,i);return t===0?"noop":t<0?"upgrade":"downgrade"}var P=".avatar-pack-manifest.json",g=".pack-version";function I(r){return typeof r!="object"||r===null||!("version"in r)?!1:typeof r.version=="string"}function k(r){let n;try{n=JSON.parse(r)}catch{return null}return I(n)?n:null}0&&(module.exports={PACK_MANIFEST_FILENAME,PACK_VERSION_FILENAME,classifyPackVersionTransition,parsePackManifest,parseSemVerTag});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { classifyPackVersionTransition, type PackVersionTransition, } from "../lib/compare-pack-semver-and-detect-downgrade.js";
|
|
2
|
+
export { PACK_MANIFEST_FILENAME, PACK_VERSION_FILENAME, type PackManifest, type ParsedPackManifest, parsePackManifest, } from "../lib/pack-manifest-contract.js";
|
|
3
|
+
export { parseSemVerTag } from "../lib/pick-latest-stable-semver-tag.js";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @nalvietnam/avatar-cli — built with tsup
|
|
2
|
+
var m=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;function l(r){let n=r.match(m);if(!n)return null;let[,e,t,i,s]=n;return{raw:r,major:Number.parseInt(e??"0",10),minor:Number.parseInt(t??"0",10),patch:Number.parseInt(i??"0",10),prerelease:s??null}}function d(r,n){if(r===null&&n===null)return 0;if(r===null)return 1;if(n===null)return-1;let e=r.split("."),t=n.split("."),i=Math.max(e.length,t.length);for(let s=0;s<i;s++){let a=e[s],o=t[s];if(a===void 0)return-1;if(o===void 0)return 1;let u=/^\d+$/.test(a),c=/^\d+$/.test(o);if(u&&c){let p=Number(a)-Number(o);if(p!==0)return p<0?-1:1;continue}if(u!==c)return u?-1:1;if(a!==o)return a<o?-1:1}return 0}function P(r,n){return r.major!==n.major?r.major<n.major?-1:1:r.minor!==n.minor?r.minor<n.minor?-1:1:r.patch!==n.patch?r.patch<n.patch?-1:1:d(r.prerelease,n.prerelease)}function f(r){if(typeof r!="string")return null;let n=r.trim();return n===""?null:l(n)}function g(r,n){let e=f(r),t=f(n);if(e===null||t===null)return"unknown";let i=P(e,t);return i===0?"noop":i<0?"upgrade":"downgrade"}var k=".avatar-pack-manifest.json",h=".pack-version";function V(r){return typeof r!="object"||r===null||!("version"in r)?!1:typeof r.version=="string"}function M(r){let n;try{n=JSON.parse(r)}catch{return null}return V(n)?n:null}export{k as PACK_MANIFEST_FILENAME,h as PACK_VERSION_FILENAME,g as classifyPackVersionTransition,M as parsePackManifest,l as parseSemVerTag};
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/pick-latest-stable-semver-tag.ts","../../src/lib/compare-pack-semver-and-detect-downgrade.ts","../../src/lib/pack-manifest-contract.ts"],"sourcesContent":["// Pick latest stable SemVer tag from a list — used by `avatar sync` để chọn\n// pack version mới nhất một cách độc lập với pack team.\n//\n// Background:\n// Trước v1.7.1 Avatar dùng `git tags` rồi `tags[length-1]` → alphabetical sort.\n// Hoạt động OK với v0.1 → v0.4, NHƯNG break silently khi:\n// - v0.10.0 < v0.4.0 (alphabetical)\n// - v1.0.0 < v0.9.0 (alphabetical)\n// - Pre-release v0.5.0-rc1 lẫn vào (user không muốn pick auto)\n//\n// v1.7.1 fix: parse SemVer chuẩn, sort numeric, default SKIP pre-release.\n// User muốn pre-release → pass `--version v0.5.0-rc1` explicit.\n//\n// Decision (Q1=SemVer sort, Q2=skip pre-release):\n// - Parse `vMAJOR.MINOR.PATCH` (optional 'v' prefix, optional `-PRERELEASE`)\n// - Tags không match SemVer pattern → skip (vd \"release-1\", \"2026-05-29\")\n// - Default skip pre-release (có dấu `-` sau PATCH)\n// - Sort numeric tăng dần [major, minor, patch] → return tag cuối\n\n// Strict SemVer regex: optional v prefix, 3 số, optional -PRERELEASE.\n// Pre-release matches: -rc1, -beta.2, -alpha, ...\nconst SEMVER_REGEX = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?$/;\n\ninterface ParsedSemVer {\n raw: string; // Original tag string (vd \"v0.4.0\")\n major: number;\n minor: number;\n patch: number;\n prerelease: string | null; // null cho stable, string cho rc/beta/alpha\n}\n\n// Parse 1 tag string. Trả null nếu không match SemVer.\nexport function parseSemVerTag(tag: string): ParsedSemVer | null {\n const match = tag.match(SEMVER_REGEX);\n if (!match) return null;\n const [, major, minor, patch, prerelease] = match;\n return {\n raw: tag,\n major: Number.parseInt(major ?? \"0\", 10),\n minor: Number.parseInt(minor ?? \"0\", 10),\n patch: Number.parseInt(patch ?? \"0\", 10),\n prerelease: prerelease ?? null,\n };\n}\n\n// Pick latest stable tag từ array. Mặc định skip pre-release.\n// includePrerelease=true → cho phép pre-release (vd CI test bleeding edge).\n//\n// Trả null nếu KHÔNG có tag SemVer nào (caller xử lý: error hoặc fallback).\nexport function pickLatestStableSemVerTag(\n tags: string[],\n includePrerelease = false,\n): string | null {\n const parsed = tags\n .map(parseSemVerTag)\n .filter((t): t is ParsedSemVer => t !== null)\n .filter((t) => includePrerelease || t.prerelease === null);\n\n if (parsed.length === 0) return null;\n\n // Sort tăng dần: [major, minor, patch] numeric.\n parsed.sort((a, b) => {\n if (a.major !== b.major) return a.major - b.major;\n if (a.minor !== b.minor) return a.minor - b.minor;\n return a.patch - b.patch;\n });\n\n // Last element = latest.\n return parsed[parsed.length - 1]?.raw ?? null;\n}\n","// So sánh 2 version pack theo SemVer 2.0.0 §11 để phát hiện HẠ CẤP (ticket #191).\n//\n// Background:\n// `avatar sync` trần luôn kéo bản stable mới nhất. Máy đang cài bản beta MỚI HƠN\n// (vd v1.1.41-beta.0) chạy `avatar sync` sẽ bị đưa về stable cũ hơn (v1.1.40) mà\n// KHÔNG có dấu hiệu nào. Module này chỉ PHÂN LOẠI chuyển version; caller\n// (`src/commands/sync.ts`) quyết định in cảnh báo — hành vi mặc định không đổi.\n//\n// Vì sao không dùng `pickLatestStableSemVerTag`:\n// Hàm đó sort theo [major, minor, patch] và BỎ QUA prerelease\n// (`pick-latest-stable-semver-tag.ts:62-66`) ⇒ `v1.1.38-beta.0` và `v1.1.38` ra\n// \"bằng nhau\". Sai với §11. Ở đây chỉ dùng lại `parseSemVerTag` cho khâu PARSE\n// (DRY — regex đã có test riêng), phần so sánh tự viết đủ §11.\n//\n// Không thêm dependency (`semver` npm) — bất biến của ticket #191.\n\nimport { parseSemVerTag } from \"./pick-latest-stable-semver-tag.js\";\n\n// `ParsedSemVer` không được export ở module gốc ⇒ suy ra kiểu từ chính hàm parse,\n// tránh phải sửa file thứ 3 chỉ để lấy 1 interface.\ntype Parsed = NonNullable<ReturnType<typeof parseSemVerTag>>;\n\nexport type PackVersionTransition = \"upgrade\" | \"noop\" | \"downgrade\" | \"unknown\";\n\n// So 2 chuỗi prerelease theo SemVer 2.0.0 §11:\n// - stable (null) > prerelease\n// - identifier toàn số so bằng SỐ (beta.10 > beta.2, KHÔNG lexical)\n// - identifier số < identifier chữ\n// - còn lại so lexical ASCII\n// - tập ÍT field < tập NHIỀU field khi phần đầu bằng nhau (beta < beta.0)\nfunction comparePrereleaseIdentifiers(a: string | null, b: string | null): number {\n if (a === null && b === null) return 0;\n if (a === null) return 1;\n if (b === null) return -1;\n\n const left = a.split(\".\");\n const right = b.split(\".\");\n const length = Math.max(left.length, right.length);\n\n for (let i = 0; i < length; i++) {\n const x = left[i];\n const y = right[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n\n const xIsNumeric = /^\\d+$/.test(x);\n const yIsNumeric = /^\\d+$/.test(y);\n if (xIsNumeric && yIsNumeric) {\n const diff = Number(x) - Number(y);\n if (diff !== 0) return diff < 0 ? -1 : 1;\n continue;\n }\n if (xIsNumeric !== yIsNumeric) return xIsNumeric ? -1 : 1;\n if (x !== y) return x < y ? -1 : 1;\n }\n\n return 0;\n}\n\n// So đầy đủ 2 version đã parse. Trả -1 / 0 / 1.\nfunction comparePackSemVer(a: Parsed, b: Parsed): number {\n if (a.major !== b.major) return a.major < b.major ? -1 : 1;\n if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1;\n if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1;\n return comparePrereleaseIdentifiers(a.prerelease, b.prerelease);\n}\n\n// Parse fail-soft: mọi input không phải chuỗi SemVer đều thành null (⇒ \"unknown\").\n// Trim vì `.pack-version` có thể còn `\\n` cuối file, và regex gốc neo `^…$` sẽ\n// từ chối chuỗi có newline.\nfunction parsePackVersion(value: string | null | undefined): Parsed | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n if (trimmed === \"\") return null;\n return parseSemVerTag(trimmed);\n}\n\n// Phân loại chuyển version: bản ĐANG CÀI (`current`) → bản SẮP CÀI (`target`).\n//\n// `current` lớn hơn `target` ⇒ \"downgrade\" (đây là ca #191 cần cảnh báo).\n// Bất kỳ phía nào không đọc được (chưa cài, `local:/path`, `local-2026-08-11`,\n// `+build`, rỗng, null) ⇒ \"unknown\" — caller phải IM LẶNG, không đoán.\nexport function classifyPackVersionTransition(\n current: string | null | undefined,\n target: string | null | undefined,\n): PackVersionTransition {\n const parsedCurrent = parsePackVersion(current);\n const parsedTarget = parsePackVersion(target);\n if (parsedCurrent === null || parsedTarget === null) return \"unknown\";\n\n const comparison = comparePackSemVer(parsedCurrent, parsedTarget);\n if (comparison === 0) return \"noop\";\n return comparison < 0 ? \"upgrade\" : \"downgrade\";\n}\n","// Hợp đồng manifest pack — phần THUẦN của `pack-manifest-store.ts`: tên file, hình\n// dạng dữ liệu, và phép đọc/validate từ chuỗi JSON. KHÔNG chạm filesystem.\n//\n// Vì sao tách khỏi `pack-manifest-store.ts` (#292):\n// Extension VSCode cần đúng phần kiến thức này (biết manifest tên gì, có field\n// gì) nhưng đọc file bằng `fs.readFileSync` ĐỒNG BỘ — nó gọi trong constructor\n// của `VersionCheckController`, đổi sang async sẽ lan ra cả đường `activate()`.\n// `pack-manifest-store.ts` lại import `filesystem-helpers.js`, mà file đó kéo\n// theo `node:crypto` + `node:child_process` + cả đường tải/giải nén tarball.\n// Nhét nguyên cụm đó vào bundle extension là vô cớ, nên tách hợp đồng thuần ra\n// đây: một bản implement cho kiến thức, hai lớp vỏ IO (async ở CLI, sync ở\n// extension).\n//\n// ⛔ KHÔNG thêm import `node:*` vào file này — nó nằm trong `src/core/index.ts`\n// (mặt tiền library) và phải bundle được vào extension mà không kéo theo gì.\n\nexport const PACK_MANIFEST_FILENAME = \".avatar-pack-manifest.json\";\nexport const PACK_VERSION_FILENAME = \".pack-version\";\n\nexport interface PackManifest {\n version: string; // vd \"v0.2.0\"\n downloadedObject: string; // vd \"pack-v0.2.0.tar.gz\"\n sha256?: string; // hash tarball đã tải (nếu Supabase cung cấp)\n extractedAt: string; // ISO timestamp\n}\n\n/**\n * Hình dạng ĐỌC được từ đĩa — khác hình dạng GHI ở chỗ chỉ `version` được kiểm.\n *\n * Vì sao không dùng thẳng `PackManifest`: manifest do bản CLI cũ (hoặc pack cài\n * tay) ghi có thể thiếu `downloadedObject`/`extractedAt`. Siết đủ 3 field sẽ biến\n * những manifest đó thành \"không có manifest\" một cách IM LẶNG. Nhưng khai kiểu\n * trả về là `PackManifest` thì type nói dối: TypeScript hứa `downloadedObject` là\n * `string` trong khi runtime có thể `undefined`. Tách kiểu ra là cách giữ cả hai —\n * đường GHI vẫn bị bắt buộc đủ field, đường ĐỌC nói đúng những gì nó kiểm.\n */\nexport type ParsedPackManifest = Pick<PackManifest, \"version\"> &\n Partial<Omit<PackManifest, \"version\">>;\n\n// Nhận biết object có `version` là chuỗi — điều kiện cần DUY NHẤT, xem lý do ở\n// docstring của `ParsedPackManifest`.\nfunction hasStringVersion(value: unknown): value is ParsedPackManifest {\n if (typeof value !== \"object\" || value === null || !(\"version\" in value)) return false;\n return typeof (value as { version: unknown }).version === \"string\";\n}\n\n/**\n * Đọc manifest từ chuỗi JSON thô.\n *\n * JSON hỏng / không phải object / thiếu `version` ⇒ `null`, KHÔNG throw:\n * workspace vừa `avatar init` mà chưa `sync` là trạng thái HỢP LỆ, không phải lỗi.\n */\nexport function parsePackManifest(raw: string): ParsedPackManifest | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n return hasStringVersion(parsed) ? parsed : null;\n}\n"],"mappings":";AAqBA,IAAMA,EAAe,gDAWd,SAASC,EAAeC,EAAkC,CAC/D,IAAMC,EAAQD,EAAI,MAAMF,CAAY,EACpC,GAAI,CAACG,EAAO,OAAO,KACnB,GAAM,CAAC,CAAEC,EAAOC,EAAOC,EAAOC,CAAU,EAAIJ,EAC5C,MAAO,CACL,IAAKD,EACL,MAAO,OAAO,SAASE,GAAS,IAAK,EAAE,EACvC,MAAO,OAAO,SAASC,GAAS,IAAK,EAAE,EACvC,MAAO,OAAO,SAASC,GAAS,IAAK,EAAE,EACvC,WAAYC,GAAc,IAC5B,CACF,CCbA,SAASC,EAA6BC,EAAkBC,EAA0B,CAChF,GAAID,IAAM,MAAQC,IAAM,KAAM,MAAO,GACrC,GAAID,IAAM,KAAM,MAAO,GACvB,GAAIC,IAAM,KAAM,MAAO,GAEvB,IAAMC,EAAOF,EAAE,MAAM,GAAG,EAClBG,EAAQF,EAAE,MAAM,GAAG,EACnBG,EAAS,KAAK,IAAIF,EAAK,OAAQC,EAAM,MAAM,EAEjD,QAASE,EAAI,EAAGA,EAAID,EAAQC,IAAK,CAC/B,IAAMC,EAAIJ,EAAKG,CAAC,EACVE,EAAIJ,EAAME,CAAC,EACjB,GAAIC,IAAM,OAAW,MAAO,GAC5B,GAAIC,IAAM,OAAW,MAAO,GAE5B,IAAMC,EAAa,QAAQ,KAAKF,CAAC,EAC3BG,EAAa,QAAQ,KAAKF,CAAC,EACjC,GAAIC,GAAcC,EAAY,CAC5B,IAAMC,EAAO,OAAOJ,CAAC,EAAI,OAAOC,CAAC,EACjC,GAAIG,IAAS,EAAG,OAAOA,EAAO,EAAI,GAAK,EACvC,QACF,CACA,GAAIF,IAAeC,EAAY,OAAOD,EAAa,GAAK,EACxD,GAAIF,IAAMC,EAAG,OAAOD,EAAIC,EAAI,GAAK,CACnC,CAEA,MAAO,EACT,CAGA,SAASI,EAAkBX,EAAWC,EAAmB,CACvD,OAAID,EAAE,QAAUC,EAAE,MAAcD,EAAE,MAAQC,EAAE,MAAQ,GAAK,EACrDD,EAAE,QAAUC,EAAE,MAAcD,EAAE,MAAQC,EAAE,MAAQ,GAAK,EACrDD,EAAE,QAAUC,EAAE,MAAcD,EAAE,MAAQC,EAAE,MAAQ,GAAK,EAClDF,EAA6BC,EAAE,WAAYC,EAAE,UAAU,CAChE,CAKA,SAASW,EAAiBC,EAAiD,CACzE,GAAI,OAAOA,GAAU,SAAU,OAAO,KACtC,IAAMC,EAAUD,EAAM,KAAK,EAC3B,OAAIC,IAAY,GAAW,KACpBC,EAAeD,CAAO,CAC/B,CAOO,SAASE,EACdC,EACAC,EACuB,CACvB,IAAMC,EAAgBP,EAAiBK,CAAO,EACxCG,EAAeR,EAAiBM,CAAM,EAC5C,GAAIC,IAAkB,MAAQC,IAAiB,KAAM,MAAO,UAE5D,IAAMC,EAAaV,EAAkBQ,EAAeC,CAAY,EAChE,OAAIC,IAAe,EAAU,OACtBA,EAAa,EAAI,UAAY,WACtC,CC7EO,IAAMC,EAAyB,6BACzBC,EAAwB,gBAwBrC,SAASC,EAAiBC,EAA6C,CACrE,OAAI,OAAOA,GAAU,UAAYA,IAAU,MAAQ,EAAE,YAAaA,GAAe,GAC1E,OAAQA,EAA+B,SAAY,QAC5D,CAQO,SAASC,EAAkBC,EAAwC,CACxE,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAG,CACzB,MAAQ,CACN,OAAO,IACT,CACA,OAAOH,EAAiBI,CAAM,EAAIA,EAAS,IAC7C","names":["SEMVER_REGEX","parseSemVerTag","tag","match","major","minor","patch","prerelease","comparePrereleaseIdentifiers","a","b","left","right","length","i","x","y","xIsNumeric","yIsNumeric","diff","comparePackSemVer","parsePackVersion","value","trimmed","parseSemVerTag","classifyPackVersionTransition","current","target","parsedCurrent","parsedTarget","comparison","PACK_MANIFEST_FILENAME","PACK_VERSION_FILENAME","hasStringVersion","value","parsePackManifest","raw","parsed"]}
|