@nalvietnam/avatar-cli 1.2.5 → 1.2.7
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
|
@@ -546,19 +546,19 @@ function applyUseGlobal(existing, source) {
|
|
|
546
546
|
...sourceModel ? { model: sourceModel } : {}
|
|
547
547
|
};
|
|
548
548
|
}
|
|
549
|
-
async function writeClaudeSettings(workspacePath,
|
|
549
|
+
async function writeClaudeSettings(workspacePath, input5) {
|
|
550
550
|
const path = getClaudeSettingsPath(workspacePath);
|
|
551
551
|
const existing = await readExistingSettings(path);
|
|
552
552
|
let merged;
|
|
553
|
-
switch (
|
|
553
|
+
switch (input5.provider) {
|
|
554
554
|
case "subscription":
|
|
555
|
-
merged = applySubscription(existing,
|
|
555
|
+
merged = applySubscription(existing, input5.model);
|
|
556
556
|
break;
|
|
557
557
|
case "llmlite":
|
|
558
|
-
merged = applyLLMLite(existing,
|
|
558
|
+
merged = applyLLMLite(existing, input5.apiKey, input5.baseUrl, input5.model);
|
|
559
559
|
break;
|
|
560
560
|
case "use-global":
|
|
561
|
-
merged = applyUseGlobal(existing,
|
|
561
|
+
merged = applyUseGlobal(existing, input5.sourceSettings);
|
|
562
562
|
break;
|
|
563
563
|
}
|
|
564
564
|
await writeJsonAtomic(path, merged, SECRET_FILE_MODE2);
|
|
@@ -1194,8 +1194,8 @@ async function applyFixes(checks) {
|
|
|
1194
1194
|
|
|
1195
1195
|
// src/commands/init.ts
|
|
1196
1196
|
import { basename, join as join16, relative as relative2, resolve } from "path";
|
|
1197
|
-
import { confirm as confirm3, input as
|
|
1198
|
-
import
|
|
1197
|
+
import { confirm as confirm3, input as input4, select as select7 } from "@inquirer/prompts";
|
|
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;
|
|
@@ -1456,15 +1498,15 @@ var RepoAlreadyExistsError = class extends Error {
|
|
|
1456
1498
|
this.name = "RepoAlreadyExistsError";
|
|
1457
1499
|
}
|
|
1458
1500
|
};
|
|
1459
|
-
function executeGhRepoCreate(
|
|
1460
|
-
const fullName = `${
|
|
1501
|
+
function executeGhRepoCreate(input5) {
|
|
1502
|
+
const fullName = `${input5.org}/${input5.name}`;
|
|
1461
1503
|
const args = [
|
|
1462
1504
|
"repo",
|
|
1463
1505
|
"create",
|
|
1464
1506
|
fullName,
|
|
1465
|
-
`--${
|
|
1507
|
+
`--${input5.visibility}`,
|
|
1466
1508
|
"--source",
|
|
1467
|
-
|
|
1509
|
+
input5.folder,
|
|
1468
1510
|
"--remote",
|
|
1469
1511
|
"origin",
|
|
1470
1512
|
"--push"
|
|
@@ -1517,16 +1559,16 @@ function validateRepoVisibility(v) {
|
|
|
1517
1559
|
}
|
|
1518
1560
|
|
|
1519
1561
|
// src/lib/create-github-remote-from-folder.ts
|
|
1520
|
-
function createGithubRemoteFromFolder(
|
|
1521
|
-
validateRepoName(
|
|
1522
|
-
validateRepoVisibility(
|
|
1523
|
-
const org =
|
|
1524
|
-
log.info(`T\u1EA1o GitHub repo ${org}/${
|
|
1562
|
+
function createGithubRemoteFromFolder(input5) {
|
|
1563
|
+
validateRepoName(input5.name);
|
|
1564
|
+
validateRepoVisibility(input5.visibility);
|
|
1565
|
+
const org = input5.org ?? resolveGithubUsernameDefault();
|
|
1566
|
+
log.info(`T\u1EA1o GitHub repo ${org}/${input5.name} (${input5.visibility})...`);
|
|
1525
1567
|
const urls = executeGhRepoCreate({
|
|
1526
|
-
folder:
|
|
1568
|
+
folder: input5.folder,
|
|
1527
1569
|
org,
|
|
1528
|
-
name:
|
|
1529
|
-
visibility:
|
|
1570
|
+
name: input5.name,
|
|
1571
|
+
visibility: input5.visibility
|
|
1530
1572
|
});
|
|
1531
1573
|
log.success(`\u0110\xE3 t\u1EA1o: ${urls.sshUrl}`);
|
|
1532
1574
|
return urls;
|
|
@@ -1568,7 +1610,7 @@ function detectPackageManager() {
|
|
|
1568
1610
|
|
|
1569
1611
|
// src/lib/handle-remote-access-failure-with-account-switch.ts
|
|
1570
1612
|
import { spawnSync as spawnSync12 } from "child_process";
|
|
1571
|
-
import { select as select5 } from "@inquirer/prompts";
|
|
1613
|
+
import { input as input3, select as select5 } from "@inquirer/prompts";
|
|
1572
1614
|
|
|
1573
1615
|
// src/lib/verify-git-remote-accessible.ts
|
|
1574
1616
|
import { spawnSync as spawnSync11 } from "child_process";
|
|
@@ -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) {
|
|
@@ -1628,33 +1670,43 @@ function getReasonHint(reason, url, ghUser) {
|
|
|
1628
1670
|
case "no-access":
|
|
1629
1671
|
return ghUser ? `Repo c\xF3 th\u1EC3 private v\xE0 account '${ghUser}' kh\xF4ng c\xF3 quy\u1EC1n access. Switch sang account c\xF3 quy\u1EC1n, ho\u1EB7c xin invite t\u1EEB owner repo.` : "Repo c\xF3 th\u1EC3 private v\xE0 gh CLI ch\u01B0a login. Login tr\u01B0\u1EDBc r\u1ED3i retry.";
|
|
1630
1672
|
case "not-found":
|
|
1631
|
-
return `URL c\xF3 th\u1EC3 sai ch\xEDnh t\u1EA3, ho\u1EB7c repo \u0111\xE3 b\u1ECB x\xF3a/rename. Check: ${url}`;
|
|
1673
|
+
return `URL c\xF3 th\u1EC3 sai ch\xEDnh t\u1EA3, ho\u1EB7c repo \u0111\xE3 b\u1ECB x\xF3a/rename. Nh\u1EADp l\u1EA1i URL \u0111\xFAng. Check: ${url}`;
|
|
1632
1674
|
case "network":
|
|
1633
1675
|
return "Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c GitHub. Check m\u1EA1ng / VPN / firewall.";
|
|
1634
1676
|
case "timeout":
|
|
1635
1677
|
return "Network ch\u1EADm > 5s. Check m\u1EA1ng r\u1ED3i retry.";
|
|
1636
1678
|
default:
|
|
1637
|
-
return "L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh.
|
|
1679
|
+
return "L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh. URL c\xF3 th\u1EC3 sai \u2014 nh\u1EADp l\u1EA1i, ho\u1EB7c check gh auth status.";
|
|
1638
1680
|
}
|
|
1639
1681
|
}
|
|
1682
|
+
function isValidGitUrl(url) {
|
|
1683
|
+
const trimmed = url.trim();
|
|
1684
|
+
if (!trimmed) return false;
|
|
1685
|
+
return /^https?:\/\/[\w.@/-]+$/.test(trimmed) || /^git@[\w.-]+:[\w./-]+\.git$/.test(trimmed) || /^[\w.-]+\/[\w.-]+$/.test(trimmed);
|
|
1686
|
+
}
|
|
1640
1687
|
async function handleRemoteAccessFailureWithAccountSwitch(args) {
|
|
1688
|
+
let currentUrl = args.url;
|
|
1641
1689
|
let reason = args.initialReason;
|
|
1642
1690
|
let detail = args.initialDetail;
|
|
1643
1691
|
while (true) {
|
|
1644
1692
|
const ghUser = getCurrentGhUser2();
|
|
1645
|
-
log.warn(`Kh\xF4ng truy c\u1EADp \u0111\u01B0\u1EE3c ${
|
|
1693
|
+
log.warn(`Kh\xF4ng truy c\u1EADp \u0111\u01B0\u1EE3c ${currentUrl}`);
|
|
1646
1694
|
log.dim(` L\xFD do: ${reason}${detail ? ` \u2014 ${detail.slice(0, 150)}` : ""}`);
|
|
1647
|
-
log.info(getReasonHint(reason,
|
|
1695
|
+
log.info(getReasonHint(reason, currentUrl, ghUser));
|
|
1648
1696
|
if (ghUser) log.dim(` gh CLI hi\u1EC7n \u0111ang login: ${ghUser}`);
|
|
1649
1697
|
const action = await select5({
|
|
1650
1698
|
message: "C\xE1ch x\u1EED l\xFD?",
|
|
1651
1699
|
choices: [
|
|
1700
|
+
{
|
|
1701
|
+
name: "Nh\u1EADp l\u1EA1i URL \u0111\xFAng (recommended khi URL sai ch\xEDnh t\u1EA3)",
|
|
1702
|
+
value: "re-input-url"
|
|
1703
|
+
},
|
|
1652
1704
|
{
|
|
1653
1705
|
name: "Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",
|
|
1654
1706
|
value: "switch"
|
|
1655
1707
|
},
|
|
1656
1708
|
{
|
|
1657
|
-
name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa
|
|
1709
|
+
name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa permission) \u2014 retry verify",
|
|
1658
1710
|
value: "retry"
|
|
1659
1711
|
},
|
|
1660
1712
|
{
|
|
@@ -1665,17 +1717,25 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
|
|
|
1665
1717
|
});
|
|
1666
1718
|
if (action === "abort") {
|
|
1667
1719
|
throw new RemoteAccessAbortedError(
|
|
1668
|
-
`User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${
|
|
1720
|
+
`User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${currentUrl} r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar init'.`
|
|
1669
1721
|
);
|
|
1670
1722
|
}
|
|
1723
|
+
if (action === "re-input-url") {
|
|
1724
|
+
const newUrl = await input3({
|
|
1725
|
+
message: "URL git remote (https://github.com/owner/repo.git ho\u1EB7c git@github.com:owner/repo.git):",
|
|
1726
|
+
default: currentUrl,
|
|
1727
|
+
validate: (v) => isValidGitUrl(v) || "URL kh\xF4ng \u0111\xFAng format git remote"
|
|
1728
|
+
});
|
|
1729
|
+
currentUrl = newUrl.trim();
|
|
1730
|
+
}
|
|
1671
1731
|
if (action === "switch") {
|
|
1672
|
-
|
|
1732
|
+
triggerGhAuthLoginInteractive2();
|
|
1673
1733
|
}
|
|
1674
|
-
log.info(
|
|
1675
|
-
const result = tryVerifyGitRemoteAccessible(
|
|
1734
|
+
log.info(`Verify remote l\u1EA1i: ${currentUrl}...`);
|
|
1735
|
+
const result = tryVerifyGitRemoteAccessible(currentUrl);
|
|
1676
1736
|
if (result.ok) {
|
|
1677
|
-
log.success(`Remote accessible: ${
|
|
1678
|
-
return;
|
|
1737
|
+
log.success(`Remote accessible: ${currentUrl}`);
|
|
1738
|
+
return { resolvedUrl: currentUrl };
|
|
1679
1739
|
}
|
|
1680
1740
|
reason = result.reason ?? "unknown";
|
|
1681
1741
|
detail = result.detail;
|
|
@@ -1792,14 +1852,16 @@ async function ensureGitHubReady(remoteUrl) {
|
|
|
1792
1852
|
const result = tryVerifyGitRemoteAccessible(remoteUrl);
|
|
1793
1853
|
if (result.ok) {
|
|
1794
1854
|
log.success(`Remote accessible: ${remoteUrl}`);
|
|
1795
|
-
|
|
1796
|
-
await handleRemoteAccessFailureWithAccountSwitch({
|
|
1797
|
-
url: remoteUrl,
|
|
1798
|
-
initialReason: result.reason ?? "unknown",
|
|
1799
|
-
initialDetail: result.detail
|
|
1800
|
-
});
|
|
1855
|
+
return { resolvedRemoteUrl: remoteUrl };
|
|
1801
1856
|
}
|
|
1857
|
+
const recovered = await handleRemoteAccessFailureWithAccountSwitch({
|
|
1858
|
+
url: remoteUrl,
|
|
1859
|
+
initialReason: result.reason ?? "unknown",
|
|
1860
|
+
initialDetail: result.detail
|
|
1861
|
+
});
|
|
1862
|
+
return { resolvedRemoteUrl: recovered.resolvedUrl };
|
|
1802
1863
|
}
|
|
1864
|
+
return {};
|
|
1803
1865
|
}
|
|
1804
1866
|
|
|
1805
1867
|
// src/lib/create-workspace-remote-via-gh.ts
|
|
@@ -1828,27 +1890,27 @@ function canCreateInNamespace(org, ghUser) {
|
|
|
1828
1890
|
reason: `'${ghUser}' kh\xF4ng ph\u1EA3i member c\u1EE7a org '${org}'. Li\xEAn h\u1EC7 admin org \u0111\u1EC3 \u0111\u01B0\u1EE3c invite, ho\u1EB7c pass --repo-org=${ghUser} t\u1EA1o d\u01B0\u1EDBi personal account.`
|
|
1829
1891
|
};
|
|
1830
1892
|
}
|
|
1831
|
-
async function createWorkspaceRemoteViaGh(
|
|
1832
|
-
validateRepoName(
|
|
1833
|
-
validateRepoVisibility(
|
|
1893
|
+
async function createWorkspaceRemoteViaGh(input5) {
|
|
1894
|
+
validateRepoName(input5.workspaceName);
|
|
1895
|
+
validateRepoVisibility(input5.visibility);
|
|
1834
1896
|
await ensureGitHubReady();
|
|
1835
1897
|
const ghUser = resolveGithubUsernameDefault();
|
|
1836
|
-
const org =
|
|
1898
|
+
const org = input5.org ?? ghUser;
|
|
1837
1899
|
const namespaceCheck = canCreateInNamespace(org, ghUser);
|
|
1838
1900
|
if (!namespaceCheck.ok) {
|
|
1839
1901
|
throw new Error(`Kh\xF4ng th\u1EC3 t\u1EA1o repo d\u01B0\u1EDBi '${org}/': ${namespaceCheck.reason}`);
|
|
1840
1902
|
}
|
|
1841
|
-
const fullName = `${org}/${
|
|
1842
|
-
log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${
|
|
1903
|
+
const fullName = `${org}/${input5.workspaceName}`;
|
|
1904
|
+
log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input5.visibility})...`);
|
|
1843
1905
|
const r = spawnSync16(
|
|
1844
1906
|
"gh",
|
|
1845
1907
|
[
|
|
1846
1908
|
"repo",
|
|
1847
1909
|
"create",
|
|
1848
1910
|
fullName,
|
|
1849
|
-
`--${
|
|
1911
|
+
`--${input5.visibility}`,
|
|
1850
1912
|
"--source",
|
|
1851
|
-
|
|
1913
|
+
input5.workspacePath,
|
|
1852
1914
|
"--remote",
|
|
1853
1915
|
"origin",
|
|
1854
1916
|
"--push"
|
|
@@ -1860,7 +1922,7 @@ async function createWorkspaceRemoteViaGh(input4) {
|
|
|
1860
1922
|
if (stderr) process.stderr.write(`${stderr}
|
|
1861
1923
|
`);
|
|
1862
1924
|
throw new Error(
|
|
1863
|
-
`T\u1EA1o workspace remote th\u1EA5t b\u1EA1i (exit ${r.status}). Workspace v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c local. Setup remote sau qua: gh repo create ${fullName} --${
|
|
1925
|
+
`T\u1EA1o workspace remote th\u1EA5t b\u1EA1i (exit ${r.status}). Workspace v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c local. Setup remote sau qua: gh repo create ${fullName} --${input5.visibility} --source=. --remote=origin --push`
|
|
1864
1926
|
);
|
|
1865
1927
|
}
|
|
1866
1928
|
const sshUrl = `git@github.com:${fullName}.git`;
|
|
@@ -2176,7 +2238,7 @@ function buildScaffoldVariables(args) {
|
|
|
2176
2238
|
}
|
|
2177
2239
|
|
|
2178
2240
|
// src/commands/login.ts
|
|
2179
|
-
import
|
|
2241
|
+
import boxen3 from "boxen";
|
|
2180
2242
|
import open from "open";
|
|
2181
2243
|
|
|
2182
2244
|
// src/lib/google-oauth-device-flow.ts
|
|
@@ -2323,7 +2385,7 @@ async function runLogin(opts) {
|
|
|
2323
2385
|
"",
|
|
2324
2386
|
`Ho\u1EB7c Avatar t\u1EF1 m\u1EDF browser, click ${chalk.green("Allow")}...`
|
|
2325
2387
|
].join("\n");
|
|
2326
|
-
process.stdout.write(`${
|
|
2388
|
+
process.stdout.write(`${boxen3(instructions, { padding: 1, borderStyle: "round" })}
|
|
2327
2389
|
`);
|
|
2328
2390
|
void open(verificationUrl).catch(() => {
|
|
2329
2391
|
log.dim("(Kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c browser t\u1EF1 \u0111\u1ED9ng \u2014 copy URL \u1EDF tr\xEAn)");
|
|
@@ -2458,14 +2520,15 @@ async function promptProjectStatus() {
|
|
|
2458
2520
|
});
|
|
2459
2521
|
}
|
|
2460
2522
|
async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
2461
|
-
const
|
|
2523
|
+
const initialRemoteUrl = opts.clientRepo ?? await input4({
|
|
2462
2524
|
message: "URL git c\u1EE7a repo:",
|
|
2463
2525
|
validate: (v) => v.length > 0 ? true : "URL b\u1EAFt bu\u1ED9c"
|
|
2464
2526
|
});
|
|
2465
|
-
await ensureGitHubReady(
|
|
2527
|
+
const { resolvedRemoteUrl } = await ensureGitHubReady(initialRemoteUrl);
|
|
2528
|
+
const remoteUrl = resolvedRemoteUrl ?? initialRemoteUrl;
|
|
2466
2529
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
2467
2530
|
const inferredName = inferWorkspaceName(remoteUrl);
|
|
2468
|
-
const workspaceName = opts.workspaceName ?? await
|
|
2531
|
+
const workspaceName = opts.workspaceName ?? await input4({ message: "T\xEAn workspace:", default: inferredName });
|
|
2469
2532
|
const workspaceParent = resolve(opts.workspaceParent ?? ".");
|
|
2470
2533
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
2471
2534
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
@@ -2482,12 +2545,13 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
2482
2545
|
repoVisibility: opts.repoVisibility,
|
|
2483
2546
|
repoOrg: opts.repoOrg,
|
|
2484
2547
|
flow: "existing-remote",
|
|
2485
|
-
aiSkip: opts.aiSkip
|
|
2548
|
+
aiSkip: opts.aiSkip,
|
|
2549
|
+
ssoEmail: ownerEmail
|
|
2486
2550
|
});
|
|
2487
2551
|
}
|
|
2488
2552
|
async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
2489
2553
|
const folderPath = resolve(
|
|
2490
|
-
opts.folderPath ?? await
|
|
2554
|
+
opts.folderPath ?? await input4({
|
|
2491
2555
|
message: "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3:",
|
|
2492
2556
|
validate: (v) => v.length > 0 ? true : "Path b\u1EAFt bu\u1ED9c"
|
|
2493
2557
|
})
|
|
@@ -2499,7 +2563,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
2499
2563
|
const remoteUrl = await getOrCreateOriginRemote(folderPath, opts);
|
|
2500
2564
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
2501
2565
|
const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;
|
|
2502
|
-
const workspaceName = opts.workspaceName ?? await
|
|
2566
|
+
const workspaceName = opts.workspaceName ?? await input4({ message: "T\xEAn workspace:", default: inferredName });
|
|
2503
2567
|
const workspaceParent = resolve(opts.workspaceParent ?? ".");
|
|
2504
2568
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
2505
2569
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
@@ -2517,12 +2581,13 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
2517
2581
|
repoVisibility: opts.repoVisibility,
|
|
2518
2582
|
repoOrg: opts.repoOrg,
|
|
2519
2583
|
flow: "existing-folder",
|
|
2520
|
-
aiSkip: opts.aiSkip
|
|
2584
|
+
aiSkip: opts.aiSkip,
|
|
2585
|
+
ssoEmail: ownerEmail
|
|
2521
2586
|
});
|
|
2522
2587
|
}
|
|
2523
2588
|
async function runInitFromScratch(opts, ownerEmail) {
|
|
2524
2589
|
await ensureGitHubReady();
|
|
2525
|
-
const projectName = opts.workspaceName ?? await
|
|
2590
|
+
const projectName = opts.workspaceName ?? await input4({
|
|
2526
2591
|
message: "T\xEAn d\u1EF1 \xE1n:",
|
|
2527
2592
|
validate: (v) => v.length > 0 ? true : "T\xEAn b\u1EAFt bu\u1ED9c"
|
|
2528
2593
|
});
|
|
@@ -2554,9 +2619,11 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
2554
2619
|
await git(workspacePath).subModule(["add", urls.sshUrl, "src"]);
|
|
2555
2620
|
let pinnedTag = "HEAD";
|
|
2556
2621
|
if (!opts.skipTeamPack) {
|
|
2622
|
+
sp.stop();
|
|
2557
2623
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
2558
2624
|
workspacePath,
|
|
2559
|
-
opts.packVersion
|
|
2625
|
+
opts.packVersion,
|
|
2626
|
+
ownerEmail
|
|
2560
2627
|
);
|
|
2561
2628
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
2562
2629
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -2605,7 +2672,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
|
|
|
2605
2672
|
{ name: "public", value: "public" }
|
|
2606
2673
|
]
|
|
2607
2674
|
});
|
|
2608
|
-
const repoName = await
|
|
2675
|
+
const repoName = await input4({
|
|
2609
2676
|
message: "T\xEAn repo:",
|
|
2610
2677
|
default: basename(folderPath)
|
|
2611
2678
|
});
|
|
@@ -2627,9 +2694,11 @@ async function scaffoldWorkspaceWithSrcSubmodule(args) {
|
|
|
2627
2694
|
await git(args.workspacePath).subModule(["add", args.srcRemoteUrl, "src"]);
|
|
2628
2695
|
let pinnedTag = "HEAD";
|
|
2629
2696
|
if (!args.skipTeamPack) {
|
|
2697
|
+
sp.stop();
|
|
2630
2698
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
2631
2699
|
args.workspacePath,
|
|
2632
|
-
args.packVersion
|
|
2700
|
+
args.packVersion,
|
|
2701
|
+
args.ssoEmail
|
|
2633
2702
|
);
|
|
2634
2703
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
2635
2704
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -2760,7 +2829,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
|
|
|
2760
2829
|
if (action === "use-alt" && alternative) {
|
|
2761
2830
|
return alternative;
|
|
2762
2831
|
}
|
|
2763
|
-
const newName = await
|
|
2832
|
+
const newName = await input4({
|
|
2764
2833
|
message: "T\xEAn workspace m\u1EDBi:",
|
|
2765
2834
|
validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
|
|
2766
2835
|
});
|
|
@@ -2770,7 +2839,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
|
|
|
2770
2839
|
}
|
|
2771
2840
|
}
|
|
2772
2841
|
async function promptTeamOwner(currentUserEmail) {
|
|
2773
|
-
return await
|
|
2842
|
+
return await input4({ message: "Team owner email:", default: currentUserEmail });
|
|
2774
2843
|
}
|
|
2775
2844
|
async function maybeCommitWorkspace(workspacePath, skipCommit) {
|
|
2776
2845
|
if (skipCommit) {
|
|
@@ -2806,7 +2875,7 @@ function printInitSuccessBox(rootPath, flow, aiResult = null) {
|
|
|
2806
2875
|
` ${chalk.cyan("avatar sync")} Pull team-ai-pack m\u1EDBi`,
|
|
2807
2876
|
` ${chalk.cyan("avatar uninstall")} G\u1EE1 Avatar (gi\u1EEF code)`
|
|
2808
2877
|
];
|
|
2809
|
-
process.stdout.write(`${
|
|
2878
|
+
process.stdout.write(`${boxen4(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
2810
2879
|
`);
|
|
2811
2880
|
}
|
|
2812
2881
|
|
|
@@ -2843,7 +2912,7 @@ function registerSecretsCommand(program2) {
|
|
|
2843
2912
|
// src/commands/status.ts
|
|
2844
2913
|
import { promises as fs8 } from "fs";
|
|
2845
2914
|
import { join as join18 } from "path";
|
|
2846
|
-
import
|
|
2915
|
+
import boxen5 from "boxen";
|
|
2847
2916
|
|
|
2848
2917
|
// src/lib/pack-backup-manager.ts
|
|
2849
2918
|
import { promises as fs7 } from "fs";
|
|
@@ -2921,7 +2990,7 @@ function renderStatusBox(s) {
|
|
|
2921
2990
|
`${chalk.dim("Backups:")} ${s.backupCount}`,
|
|
2922
2991
|
`${chalk.dim("Tech stack:")} ${s.techStackSummary}`
|
|
2923
2992
|
];
|
|
2924
|
-
process.stdout.write(`${
|
|
2993
|
+
process.stdout.write(`${boxen5(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
2925
2994
|
`);
|
|
2926
2995
|
}
|
|
2927
2996
|
|
|
@@ -2941,7 +3010,7 @@ function registerToolsCommand(program2) {
|
|
|
2941
3010
|
// src/commands/uninstall.ts
|
|
2942
3011
|
import { relative as relative3 } from "path";
|
|
2943
3012
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
2944
|
-
import
|
|
3013
|
+
import boxen6 from "boxen";
|
|
2945
3014
|
|
|
2946
3015
|
// src/lib/create-uninstall-backup-snapshot.ts
|
|
2947
3016
|
import { cp, mkdir, writeFile } from "fs/promises";
|
|
@@ -3095,7 +3164,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
|
|
|
3095
3164
|
}
|
|
3096
3165
|
|
|
3097
3166
|
// src/commands/uninstall.ts
|
|
3098
|
-
var CLI_VERSION = "1.2.
|
|
3167
|
+
var CLI_VERSION = "1.2.7";
|
|
3099
3168
|
function registerUninstallCommand(program2) {
|
|
3100
3169
|
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
3170
|
try {
|
|
@@ -3172,12 +3241,12 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
3172
3241
|
lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
|
|
3173
3242
|
lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
|
|
3174
3243
|
}
|
|
3175
|
-
process.stdout.write(`${
|
|
3244
|
+
process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
3176
3245
|
`);
|
|
3177
3246
|
}
|
|
3178
3247
|
|
|
3179
3248
|
// src/index.ts
|
|
3180
|
-
var CLI_VERSION2 = "1.2.
|
|
3249
|
+
var CLI_VERSION2 = "1.2.7";
|
|
3181
3250
|
var program = new Command();
|
|
3182
3251
|
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
3252
|
"beforeAll",
|