@nalvietnam/avatar-cli 1.2.5 → 1.2.6
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/dist/index.js
CHANGED
|
@@ -1195,7 +1195,7 @@ async function applyFixes(checks) {
|
|
|
1195
1195
|
// src/commands/init.ts
|
|
1196
1196
|
import { basename, join as join16, relative as relative2, resolve } from "path";
|
|
1197
1197
|
import { confirm as confirm3, input as input3, select as select7 } from "@inquirer/prompts";
|
|
1198
|
-
import
|
|
1198
|
+
import boxen4 from "boxen";
|
|
1199
1199
|
|
|
1200
1200
|
// src/lib/prompt-recovery-action-on-failure.ts
|
|
1201
1201
|
import { input as input2, select as select3 } from "@inquirer/prompts";
|
|
@@ -1227,6 +1227,7 @@ import { join as join10 } from "path";
|
|
|
1227
1227
|
// src/lib/check-team-pack-access-with-retry-loop.ts
|
|
1228
1228
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
1229
1229
|
import { confirm as confirm2, select as select4 } from "@inquirer/prompts";
|
|
1230
|
+
import boxen2 from "boxen";
|
|
1230
1231
|
function parseRepoSlugFromGitUrl(url) {
|
|
1231
1232
|
const httpsMatch = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
1232
1233
|
if (httpsMatch) return httpsMatch[1];
|
|
@@ -1244,6 +1245,13 @@ function getCurrentGhUser() {
|
|
|
1244
1245
|
if (r.status !== 0) return null;
|
|
1245
1246
|
return r.stdout.trim() || null;
|
|
1246
1247
|
}
|
|
1248
|
+
function triggerGhAuthLoginInteractive() {
|
|
1249
|
+
log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
|
|
1250
|
+
const r = spawnSync6("gh", ["auth", "login", "--web"], { stdio: "inherit" });
|
|
1251
|
+
if (r.status !== 0) {
|
|
1252
|
+
log.warn(`gh auth login exit ${r.status}. C\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1247
1255
|
async function copyInfoToClipboardWithConsent(info) {
|
|
1248
1256
|
const ok = await confirm2({
|
|
1249
1257
|
message: "Copy th\xF4ng tin (GitHub username + email) v\xE0o clipboard \u0111\u1EC3 d\xE1n v\xE0o Slack/email?",
|
|
@@ -1258,44 +1266,78 @@ async function copyInfoToClipboardWithConsent(info) {
|
|
|
1258
1266
|
log.dim(`Copy clipboard fail: ${err.message}`);
|
|
1259
1267
|
}
|
|
1260
1268
|
}
|
|
1261
|
-
function
|
|
1269
|
+
function printAccessWarningBox(repoSlug, ghUser, ssoEmail) {
|
|
1262
1270
|
const lines = [
|
|
1263
|
-
"
|
|
1271
|
+
`${chalk.red("\u26D4 KH\xD4NG C\xD3 QUY\u1EC0N ACCESS")}`,
|
|
1272
|
+
"",
|
|
1273
|
+
`Repo: ${chalk.bold(repoSlug)}`,
|
|
1274
|
+
"",
|
|
1275
|
+
"B\u1EA1n c\u1EA7n \u0111\u01B0\u1EE3c admin add v\xE0o org \u0111\u1EC3 pull team-ai-pack.",
|
|
1276
|
+
"",
|
|
1277
|
+
`${chalk.dim("Th\xF4ng tin g\u1EEDi admin:")}`,
|
|
1278
|
+
` GitHub username: ${chalk.cyan(ghUser ?? "(ch\u01B0a gh auth \u2014 ch\u1EA1y: gh auth login)")}`,
|
|
1279
|
+
` NAL email: ${chalk.cyan(ssoEmail ?? "(ch\u01B0a avatar login \u2014 ch\u1EA1y: avatar login)")}`,
|
|
1280
|
+
` Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
1281
|
+
"",
|
|
1282
|
+
`${chalk.dim("Li\xEAn h\u1EC7:")} luke@nal.vn (Slack #avatar-setup)`
|
|
1283
|
+
];
|
|
1284
|
+
process.stdout.write(
|
|
1285
|
+
`${boxen2(lines.join("\n"), { padding: 1, borderColor: "red", borderStyle: "round" })}
|
|
1286
|
+
`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
function buildAccessRequestInfo(repoSlug, ghUser, ssoEmail) {
|
|
1290
|
+
return [
|
|
1291
|
+
`Request access ${repoSlug} (NAL)`,
|
|
1264
1292
|
"",
|
|
1265
1293
|
`GitHub username: ${ghUser ?? "(ch\u01B0a gh auth \u2014 ch\u1EA1y: gh auth login)"}`,
|
|
1266
1294
|
`NAL email: ${ssoEmail ?? "(ch\u01B0a avatar login \u2014 ch\u1EA1y: avatar login)"}`,
|
|
1267
1295
|
`Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
|
|
1268
|
-
];
|
|
1269
|
-
return lines.join("\n");
|
|
1296
|
+
].join("\n");
|
|
1270
1297
|
}
|
|
1271
1298
|
async function ensureTeamPackAccessWithRetry(args) {
|
|
1272
1299
|
if (checkRepoAccess(args.repoSlug)) return true;
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
log.plain(info);
|
|
1279
|
-
log.plain("");
|
|
1280
|
-
await copyInfoToClipboardWithConsent(info);
|
|
1300
|
+
const initialGhUser = getCurrentGhUser();
|
|
1301
|
+
printAccessWarningBox(args.repoSlug, initialGhUser, args.ssoEmail ?? null);
|
|
1302
|
+
await copyInfoToClipboardWithConsent(
|
|
1303
|
+
buildAccessRequestInfo(args.repoSlug, initialGhUser, args.ssoEmail ?? null)
|
|
1304
|
+
);
|
|
1281
1305
|
while (true) {
|
|
1306
|
+
const ghUser = getCurrentGhUser();
|
|
1307
|
+
const ghUserDisplay = ghUser ?? "(ch\u01B0a gh auth)";
|
|
1282
1308
|
const action = await select4({
|
|
1283
|
-
message: "
|
|
1309
|
+
message: "C\xE1ch x\u1EED l\xFD?",
|
|
1284
1310
|
choices: [
|
|
1285
|
-
{
|
|
1286
|
-
|
|
1311
|
+
{
|
|
1312
|
+
name: `\u0110\xE3 \u0111\u01B0\u1EE3c grant access v\u1EDBi GitHub username '${ghUserDisplay}' \u2014 ki\u1EC3m tra l\u1EA1i`,
|
|
1313
|
+
value: "retry-same"
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
name: "\u0110\xE3 grant v\u1EDBi GitHub account kh\xE1c \u2014 switch gh (m\u1EDF browser)",
|
|
1317
|
+
value: "switch-account"
|
|
1318
|
+
},
|
|
1319
|
+
{
|
|
1320
|
+
name: "T\u1EA1m ng\u01B0ng \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau",
|
|
1321
|
+
value: "abort"
|
|
1322
|
+
}
|
|
1287
1323
|
]
|
|
1288
1324
|
});
|
|
1289
1325
|
if (action === "abort") {
|
|
1290
1326
|
log.dim("T\u1EA1m ng\u01B0ng. Ch\u1EA1y l\u1EA1i 'avatar init' sau khi \u0111\xE3 accept invite t\u1EEB GitHub.");
|
|
1291
1327
|
return false;
|
|
1292
1328
|
}
|
|
1329
|
+
if (action === "switch-account") {
|
|
1330
|
+
triggerGhAuthLoginInteractive();
|
|
1331
|
+
}
|
|
1293
1332
|
log.info("Ki\u1EC3m tra access...");
|
|
1294
1333
|
if (checkRepoAccess(args.repoSlug)) {
|
|
1295
|
-
|
|
1334
|
+
const finalUser = getCurrentGhUser();
|
|
1335
|
+
log.success(`\u0110\xE3 c\xF3 access v\u1EDBi '${finalUser ?? "(unknown)"}' \u2014 ti\u1EBFp t\u1EE5c.`);
|
|
1296
1336
|
return true;
|
|
1297
1337
|
}
|
|
1298
|
-
log.warn(
|
|
1338
|
+
log.warn(
|
|
1339
|
+
`V\u1EABn ch\u01B0a c\xF3 access v\u1EDBi account '${ghUser ?? "(unknown)"}'. \u0110\u1EA3m b\u1EA3o \u0111\xE3 accept email invite ho\u1EB7c switch \u0111\xFAng account.`
|
|
1340
|
+
);
|
|
1299
1341
|
}
|
|
1300
1342
|
}
|
|
1301
1343
|
|
|
@@ -1361,10 +1403,10 @@ async function readPinnedPackVersion(projectRoot) {
|
|
|
1361
1403
|
}
|
|
1362
1404
|
|
|
1363
1405
|
// src/lib/add-team-pack-submodule-with-retry-on-network-fail.ts
|
|
1364
|
-
async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag) {
|
|
1406
|
+
async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag, ssoEmail) {
|
|
1365
1407
|
while (true) {
|
|
1366
1408
|
try {
|
|
1367
|
-
const result = await addTeamPackSubmodule(projectRoot, tag);
|
|
1409
|
+
const result = await addTeamPackSubmodule(projectRoot, tag, ssoEmail);
|
|
1368
1410
|
return { pinnedTag: result.pinnedTag, skipped: false };
|
|
1369
1411
|
} catch (err) {
|
|
1370
1412
|
if (err instanceof TeamPackAccessAbortedError) throw err;
|
|
@@ -1616,7 +1658,7 @@ function getCurrentGhUser2() {
|
|
|
1616
1658
|
if (r.status !== 0) return null;
|
|
1617
1659
|
return r.stdout.trim() || null;
|
|
1618
1660
|
}
|
|
1619
|
-
function
|
|
1661
|
+
function triggerGhAuthLoginInteractive2() {
|
|
1620
1662
|
log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
|
|
1621
1663
|
const r = spawnSync12("gh", ["auth", "login", "--web"], { stdio: "inherit" });
|
|
1622
1664
|
if (r.status !== 0) {
|
|
@@ -1669,7 +1711,7 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
|
|
|
1669
1711
|
);
|
|
1670
1712
|
}
|
|
1671
1713
|
if (action === "switch") {
|
|
1672
|
-
|
|
1714
|
+
triggerGhAuthLoginInteractive2();
|
|
1673
1715
|
}
|
|
1674
1716
|
log.info("Verify remote l\u1EA1i...");
|
|
1675
1717
|
const result = tryVerifyGitRemoteAccessible(args.url);
|
|
@@ -2176,7 +2218,7 @@ function buildScaffoldVariables(args) {
|
|
|
2176
2218
|
}
|
|
2177
2219
|
|
|
2178
2220
|
// src/commands/login.ts
|
|
2179
|
-
import
|
|
2221
|
+
import boxen3 from "boxen";
|
|
2180
2222
|
import open from "open";
|
|
2181
2223
|
|
|
2182
2224
|
// src/lib/google-oauth-device-flow.ts
|
|
@@ -2323,7 +2365,7 @@ async function runLogin(opts) {
|
|
|
2323
2365
|
"",
|
|
2324
2366
|
`Ho\u1EB7c Avatar t\u1EF1 m\u1EDF browser, click ${chalk.green("Allow")}...`
|
|
2325
2367
|
].join("\n");
|
|
2326
|
-
process.stdout.write(`${
|
|
2368
|
+
process.stdout.write(`${boxen3(instructions, { padding: 1, borderStyle: "round" })}
|
|
2327
2369
|
`);
|
|
2328
2370
|
void open(verificationUrl).catch(() => {
|
|
2329
2371
|
log.dim("(Kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c browser t\u1EF1 \u0111\u1ED9ng \u2014 copy URL \u1EDF tr\xEAn)");
|
|
@@ -2482,7 +2524,8 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
2482
2524
|
repoVisibility: opts.repoVisibility,
|
|
2483
2525
|
repoOrg: opts.repoOrg,
|
|
2484
2526
|
flow: "existing-remote",
|
|
2485
|
-
aiSkip: opts.aiSkip
|
|
2527
|
+
aiSkip: opts.aiSkip,
|
|
2528
|
+
ssoEmail: ownerEmail
|
|
2486
2529
|
});
|
|
2487
2530
|
}
|
|
2488
2531
|
async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
@@ -2517,7 +2560,8 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
2517
2560
|
repoVisibility: opts.repoVisibility,
|
|
2518
2561
|
repoOrg: opts.repoOrg,
|
|
2519
2562
|
flow: "existing-folder",
|
|
2520
|
-
aiSkip: opts.aiSkip
|
|
2563
|
+
aiSkip: opts.aiSkip,
|
|
2564
|
+
ssoEmail: ownerEmail
|
|
2521
2565
|
});
|
|
2522
2566
|
}
|
|
2523
2567
|
async function runInitFromScratch(opts, ownerEmail) {
|
|
@@ -2554,9 +2598,11 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
2554
2598
|
await git(workspacePath).subModule(["add", urls.sshUrl, "src"]);
|
|
2555
2599
|
let pinnedTag = "HEAD";
|
|
2556
2600
|
if (!opts.skipTeamPack) {
|
|
2601
|
+
sp.stop();
|
|
2557
2602
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
2558
2603
|
workspacePath,
|
|
2559
|
-
opts.packVersion
|
|
2604
|
+
opts.packVersion,
|
|
2605
|
+
ownerEmail
|
|
2560
2606
|
);
|
|
2561
2607
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
2562
2608
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -2627,9 +2673,11 @@ async function scaffoldWorkspaceWithSrcSubmodule(args) {
|
|
|
2627
2673
|
await git(args.workspacePath).subModule(["add", args.srcRemoteUrl, "src"]);
|
|
2628
2674
|
let pinnedTag = "HEAD";
|
|
2629
2675
|
if (!args.skipTeamPack) {
|
|
2676
|
+
sp.stop();
|
|
2630
2677
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
2631
2678
|
args.workspacePath,
|
|
2632
|
-
args.packVersion
|
|
2679
|
+
args.packVersion,
|
|
2680
|
+
args.ssoEmail
|
|
2633
2681
|
);
|
|
2634
2682
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
2635
2683
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -2806,7 +2854,7 @@ function printInitSuccessBox(rootPath, flow, aiResult = null) {
|
|
|
2806
2854
|
` ${chalk.cyan("avatar sync")} Pull team-ai-pack m\u1EDBi`,
|
|
2807
2855
|
` ${chalk.cyan("avatar uninstall")} G\u1EE1 Avatar (gi\u1EEF code)`
|
|
2808
2856
|
];
|
|
2809
|
-
process.stdout.write(`${
|
|
2857
|
+
process.stdout.write(`${boxen4(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
2810
2858
|
`);
|
|
2811
2859
|
}
|
|
2812
2860
|
|
|
@@ -2843,7 +2891,7 @@ function registerSecretsCommand(program2) {
|
|
|
2843
2891
|
// src/commands/status.ts
|
|
2844
2892
|
import { promises as fs8 } from "fs";
|
|
2845
2893
|
import { join as join18 } from "path";
|
|
2846
|
-
import
|
|
2894
|
+
import boxen5 from "boxen";
|
|
2847
2895
|
|
|
2848
2896
|
// src/lib/pack-backup-manager.ts
|
|
2849
2897
|
import { promises as fs7 } from "fs";
|
|
@@ -2921,7 +2969,7 @@ function renderStatusBox(s) {
|
|
|
2921
2969
|
`${chalk.dim("Backups:")} ${s.backupCount}`,
|
|
2922
2970
|
`${chalk.dim("Tech stack:")} ${s.techStackSummary}`
|
|
2923
2971
|
];
|
|
2924
|
-
process.stdout.write(`${
|
|
2972
|
+
process.stdout.write(`${boxen5(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
2925
2973
|
`);
|
|
2926
2974
|
}
|
|
2927
2975
|
|
|
@@ -2941,7 +2989,7 @@ function registerToolsCommand(program2) {
|
|
|
2941
2989
|
// src/commands/uninstall.ts
|
|
2942
2990
|
import { relative as relative3 } from "path";
|
|
2943
2991
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
2944
|
-
import
|
|
2992
|
+
import boxen6 from "boxen";
|
|
2945
2993
|
|
|
2946
2994
|
// src/lib/create-uninstall-backup-snapshot.ts
|
|
2947
2995
|
import { cp, mkdir, writeFile } from "fs/promises";
|
|
@@ -3095,7 +3143,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
|
|
|
3095
3143
|
}
|
|
3096
3144
|
|
|
3097
3145
|
// src/commands/uninstall.ts
|
|
3098
|
-
var CLI_VERSION = "1.2.
|
|
3146
|
+
var CLI_VERSION = "1.2.6";
|
|
3099
3147
|
function registerUninstallCommand(program2) {
|
|
3100
3148
|
program2.command("uninstall").description("G\u1EE1 Avatar kh\u1ECFi project \u2014 backup t\u1EF1 \u0111\u1ED9ng (M11)").option("--yes", "Skip confirm prompt").option("--no-backup", "Kh\xF4ng t\u1EA1o backup tr\u01B0\u1EDBc khi x\xF3a (nguy hi\u1EC3m)").option("--keep-submodule", "Gi\u1EEF submodule .claude/pack/").option("--keep-hooks", "Gi\u1EEF git hooks post-merge, pre-push").option("--dry-run", "Hi\u1EC3n th\u1ECB danh s\xE1ch s\u1EBD x\xF3a, kh\xF4ng th\u1EF1c thi").action(async (opts) => {
|
|
3101
3149
|
try {
|
|
@@ -3172,12 +3220,12 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
3172
3220
|
lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
|
|
3173
3221
|
lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
|
|
3174
3222
|
}
|
|
3175
|
-
process.stdout.write(`${
|
|
3223
|
+
process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
3176
3224
|
`);
|
|
3177
3225
|
}
|
|
3178
3226
|
|
|
3179
3227
|
// src/index.ts
|
|
3180
|
-
var CLI_VERSION2 = "1.2.
|
|
3228
|
+
var CLI_VERSION2 = "1.2.6";
|
|
3181
3229
|
var program = new Command();
|
|
3182
3230
|
program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION2, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText(
|
|
3183
3231
|
"beforeAll",
|