@nalvietnam/avatar-cli 1.5.0 → 1.6.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/dist/index.js
CHANGED
|
@@ -473,27 +473,126 @@ async function promptAiProviderChoice(globalInfo = detectGlobalClaudeSettings())
|
|
|
473
473
|
message: "Ch\u1ECDn provider cho AI features:",
|
|
474
474
|
choices: [
|
|
475
475
|
{
|
|
476
|
-
name: "1. Claude Code Subscription (d\xF9ng quota c\xE1 nh\xE2n Anthropic)",
|
|
476
|
+
name: "1. Claude Code Subscription (d\xF9ng quota c\xE1 nh\xE2n Anthropic, OAuth login)",
|
|
477
477
|
value: "subscription"
|
|
478
478
|
},
|
|
479
479
|
{
|
|
480
|
-
name: "2. LLMLite API key (llm.nal.vn \u2014 NAL
|
|
480
|
+
name: "2. LLMLite API key (llm.nal.vn \u2014 NAL gateway, key sk-...)",
|
|
481
481
|
value: "llmlite"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "3. Anthropic API key tr\u1EF1c ti\u1EBFp (console.anthropic.com, key sk-ant-...)",
|
|
485
|
+
value: "anthropic"
|
|
482
486
|
}
|
|
483
487
|
]
|
|
484
488
|
});
|
|
485
489
|
}
|
|
486
490
|
|
|
491
|
+
// src/lib/setup-anthropic-api-key-and-model.ts
|
|
492
|
+
import { password, select as select2 } from "@inquirer/prompts";
|
|
493
|
+
var ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
494
|
+
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
495
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
496
|
+
function maskAnthropicKey(key) {
|
|
497
|
+
if (key.length <= 12) return "sk-ant-***";
|
|
498
|
+
return `${key.slice(0, 7)}...${key.slice(-4)}`;
|
|
499
|
+
}
|
|
500
|
+
function validateAnthropicKeyFormat(key) {
|
|
501
|
+
const trimmed = key.trim();
|
|
502
|
+
if (trimmed.length === 0) return "API key b\u1EAFt bu\u1ED9c";
|
|
503
|
+
if (!trimmed.startsWith("sk-ant-")) {
|
|
504
|
+
return "Anthropic API key th\u01B0\u1EDDng b\u1EAFt \u0111\u1EA7u b\u1EB1ng 'sk-ant-' (l\u1EA5y t\u1EEB console.anthropic.com).";
|
|
505
|
+
}
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
async function promptAnthropicKeyHidden() {
|
|
509
|
+
return await password({
|
|
510
|
+
message: "Anthropic API key (sk-ant-..., \u1EA9n input):",
|
|
511
|
+
mask: "*",
|
|
512
|
+
validate: validateAnthropicKeyFormat
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
async function fetchAnthropicModels(apiKey) {
|
|
516
|
+
const controller = new AbortController();
|
|
517
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
518
|
+
try {
|
|
519
|
+
const res = await fetch(`${ANTHROPIC_BASE_URL}/v1/models`, {
|
|
520
|
+
method: "GET",
|
|
521
|
+
headers: {
|
|
522
|
+
"x-api-key": apiKey,
|
|
523
|
+
"anthropic-version": ANTHROPIC_API_VERSION,
|
|
524
|
+
Accept: "application/json"
|
|
525
|
+
},
|
|
526
|
+
signal: controller.signal
|
|
527
|
+
});
|
|
528
|
+
if (res.status === 401) {
|
|
529
|
+
throw new Error("API key invalid (HTTP 401). Check key tr\xEAn console.anthropic.com.");
|
|
530
|
+
}
|
|
531
|
+
if (res.status === 403) {
|
|
532
|
+
throw new Error(
|
|
533
|
+
"API key b\u1ECB reject (HTTP 403). Key c\xF3 th\u1EC3 \u0111\xE3 b\u1ECB revoke ho\u1EB7c thi\u1EBFu permission."
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (res.status === 429) {
|
|
537
|
+
throw new Error("Rate limit (HTTP 429). Ch\u1EDD v\xE0i gi\xE2y r\u1ED3i th\u1EED l\u1EA1i.");
|
|
538
|
+
}
|
|
539
|
+
if (!res.ok) {
|
|
540
|
+
throw new Error(`Fetch models th\u1EA5t b\u1EA1i (HTTP ${res.status}).`);
|
|
541
|
+
}
|
|
542
|
+
const json = await res.json();
|
|
543
|
+
const models = (json.data || []).map((m) => typeof m.id === "string" ? m.id : null).filter((id) => id !== null);
|
|
544
|
+
if (models.length === 0) {
|
|
545
|
+
throw new Error("Anthropic tr\u1EA3 v\u1EC1 list r\u1ED7ng. Li\xEAn h\u1EC7 support ho\u1EB7c check account.");
|
|
546
|
+
}
|
|
547
|
+
return models;
|
|
548
|
+
} catch (err) {
|
|
549
|
+
if (err.name === "AbortError") {
|
|
550
|
+
throw new Error(`Connect ${ANTHROPIC_BASE_URL} timeout sau ${FETCH_TIMEOUT_MS / 1e3}s.`);
|
|
551
|
+
}
|
|
552
|
+
throw err;
|
|
553
|
+
} finally {
|
|
554
|
+
clearTimeout(timer);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
async function promptAnthropicModelChoice(models) {
|
|
558
|
+
if (models.length === 1) {
|
|
559
|
+
log.info(`Auto-pick model: ${models[0]} (ch\u1EC9 1 model available)`);
|
|
560
|
+
return models[0];
|
|
561
|
+
}
|
|
562
|
+
const sorted = [...models].sort((a, b) => {
|
|
563
|
+
const score = (m) => {
|
|
564
|
+
const lower = m.toLowerCase();
|
|
565
|
+
if (lower.includes("sonnet")) return 0;
|
|
566
|
+
if (lower.includes("opus")) return 1;
|
|
567
|
+
if (lower.includes("haiku")) return 2;
|
|
568
|
+
return 3;
|
|
569
|
+
};
|
|
570
|
+
return score(a) - score(b);
|
|
571
|
+
});
|
|
572
|
+
return await select2({
|
|
573
|
+
message: "Ch\u1ECDn model m\u1EB7c \u0111\u1ECBnh cho project:",
|
|
574
|
+
choices: sorted.map((m) => ({ name: m, value: m }))
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async function setupAnthropicApiKeyAndModel() {
|
|
578
|
+
const apiKey = await promptAnthropicKeyHidden();
|
|
579
|
+
log.info(`Verify key (${maskAnthropicKey(apiKey)}) qua ${ANTHROPIC_BASE_URL}/v1/models...`);
|
|
580
|
+
const models = await fetchAnthropicModels(apiKey);
|
|
581
|
+
log.success(`Endpoint OK \u2014 ${models.length} models available`);
|
|
582
|
+
const model = await promptAnthropicModelChoice(models);
|
|
583
|
+
return { apiKey, baseUrl: ANTHROPIC_BASE_URL, model };
|
|
584
|
+
}
|
|
585
|
+
|
|
487
586
|
// src/lib/setup-llmlite-api-key-and-model.ts
|
|
488
|
-
import { input, password, select as
|
|
587
|
+
import { input, password as password2, select as select3 } from "@inquirer/prompts";
|
|
489
588
|
var DEFAULT_BASE_URL = "https://llm.nal.vn";
|
|
490
|
-
var
|
|
589
|
+
var FETCH_TIMEOUT_MS2 = 1e4;
|
|
491
590
|
function maskApiKey(key) {
|
|
492
591
|
if (key.length <= 8) return "sk-***";
|
|
493
592
|
return `${key.slice(0, 3)}...${key.slice(-4)}`;
|
|
494
593
|
}
|
|
495
594
|
async function promptApiKeyHidden() {
|
|
496
|
-
return await
|
|
595
|
+
return await password2({
|
|
497
596
|
message: "LLMLite API key (\u1EA9n input):",
|
|
498
597
|
mask: "*",
|
|
499
598
|
validate: (v) => v.trim().length > 0 ? true : "API key b\u1EAFt bu\u1ED9c"
|
|
@@ -509,7 +608,7 @@ async function promptBaseUrl(defaultUrl = DEFAULT_BASE_URL) {
|
|
|
509
608
|
}
|
|
510
609
|
async function fetchAvailableModels(baseUrl, apiKey) {
|
|
511
610
|
const controller = new AbortController();
|
|
512
|
-
const timer = setTimeout(() => controller.abort(),
|
|
611
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
513
612
|
try {
|
|
514
613
|
const res = await fetch(`${baseUrl}/v1/models`, {
|
|
515
614
|
method: "GET",
|
|
@@ -536,7 +635,7 @@ async function fetchAvailableModels(baseUrl, apiKey) {
|
|
|
536
635
|
return models;
|
|
537
636
|
} catch (err) {
|
|
538
637
|
if (err.name === "AbortError") {
|
|
539
|
-
throw new Error(`Connect ${baseUrl} timeout sau ${
|
|
638
|
+
throw new Error(`Connect ${baseUrl} timeout sau ${FETCH_TIMEOUT_MS2 / 1e3}s.`);
|
|
540
639
|
}
|
|
541
640
|
throw err;
|
|
542
641
|
} finally {
|
|
@@ -550,7 +649,7 @@ async function promptModelChoice(models) {
|
|
|
550
649
|
return claudeAliases[0];
|
|
551
650
|
}
|
|
552
651
|
const choiceList = claudeAliases.length > 0 ? claudeAliases : models;
|
|
553
|
-
return await
|
|
652
|
+
return await select3({
|
|
554
653
|
message: "Ch\u1ECDn model m\u1EB7c \u0111\u1ECBnh cho project:",
|
|
555
654
|
choices: choiceList.map((m) => ({ name: m, value: m }))
|
|
556
655
|
});
|
|
@@ -586,7 +685,12 @@ function applySubscription(existing, model) {
|
|
|
586
685
|
const { env: existingEnv, ...rest } = existing;
|
|
587
686
|
const merged = { ...rest, model };
|
|
588
687
|
if (existingEnv) {
|
|
589
|
-
const {
|
|
688
|
+
const {
|
|
689
|
+
ANTHROPIC_BASE_URL: _b,
|
|
690
|
+
ANTHROPIC_AUTH_TOKEN: _t,
|
|
691
|
+
ANTHROPIC_API_KEY: _k,
|
|
692
|
+
...envRest
|
|
693
|
+
} = existingEnv;
|
|
590
694
|
if (Object.keys(envRest).length > 0) {
|
|
591
695
|
merged.env = envRest;
|
|
592
696
|
}
|
|
@@ -594,16 +698,29 @@ function applySubscription(existing, model) {
|
|
|
594
698
|
return merged;
|
|
595
699
|
}
|
|
596
700
|
function applyLLMLite(existing, apiKey, baseUrl, model) {
|
|
701
|
+
const { ANTHROPIC_API_KEY: _k, ...envRest } = existing.env || {};
|
|
597
702
|
return {
|
|
598
703
|
...existing,
|
|
599
704
|
env: {
|
|
600
|
-
...
|
|
705
|
+
...envRest,
|
|
601
706
|
ANTHROPIC_BASE_URL: baseUrl,
|
|
602
707
|
ANTHROPIC_AUTH_TOKEN: apiKey
|
|
603
708
|
},
|
|
604
709
|
model
|
|
605
710
|
};
|
|
606
711
|
}
|
|
712
|
+
function applyAnthropic(existing, apiKey, baseUrl, model) {
|
|
713
|
+
const { ANTHROPIC_AUTH_TOKEN: _t, ...envRest } = existing.env || {};
|
|
714
|
+
return {
|
|
715
|
+
...existing,
|
|
716
|
+
env: {
|
|
717
|
+
...envRest,
|
|
718
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
719
|
+
ANTHROPIC_API_KEY: apiKey
|
|
720
|
+
},
|
|
721
|
+
model
|
|
722
|
+
};
|
|
723
|
+
}
|
|
607
724
|
function applyUseGlobal(existing, source) {
|
|
608
725
|
const sourceEnv = source.env || {};
|
|
609
726
|
const sourceModel = typeof source.model === "string" ? source.model : void 0;
|
|
@@ -627,6 +744,9 @@ async function writeClaudeSettings(workspacePath, input6) {
|
|
|
627
744
|
case "llmlite":
|
|
628
745
|
merged = applyLLMLite(existing, input6.apiKey, input6.baseUrl, input6.model);
|
|
629
746
|
break;
|
|
747
|
+
case "anthropic":
|
|
748
|
+
merged = applyAnthropic(existing, input6.apiKey, input6.baseUrl, input6.model);
|
|
749
|
+
break;
|
|
630
750
|
case "use-global":
|
|
631
751
|
merged = applyUseGlobal(existing, input6.sourceSettings);
|
|
632
752
|
break;
|
|
@@ -701,6 +821,23 @@ async function runAiSetupPhase(args) {
|
|
|
701
821
|
log.success(`AI ready \xB7 LLMLite \xB7 model=${llmConfig.model} \xB7 ${llmConfig.baseUrl}`);
|
|
702
822
|
return { ok: true, provider: "llmlite", model: llmConfig.model };
|
|
703
823
|
}
|
|
824
|
+
case "anthropic": {
|
|
825
|
+
const anthropicConfig = await setupAnthropicApiKeyAndModel();
|
|
826
|
+
await writeClaudeSettings(args.workspacePath, {
|
|
827
|
+
provider: "anthropic",
|
|
828
|
+
apiKey: anthropicConfig.apiKey,
|
|
829
|
+
baseUrl: anthropicConfig.baseUrl,
|
|
830
|
+
model: anthropicConfig.model
|
|
831
|
+
});
|
|
832
|
+
await appendAuditEntry(
|
|
833
|
+
"ai_setup",
|
|
834
|
+
`provider=anthropic,result=ok,model=${anthropicConfig.model}`
|
|
835
|
+
);
|
|
836
|
+
log.success(
|
|
837
|
+
`AI ready \xB7 Anthropic Direct \xB7 model=${anthropicConfig.model} \xB7 ${anthropicConfig.baseUrl}`
|
|
838
|
+
);
|
|
839
|
+
return { ok: true, provider: "anthropic", model: anthropicConfig.model };
|
|
840
|
+
}
|
|
704
841
|
case "use-global": {
|
|
705
842
|
if (!globalInfo.rawSettings) {
|
|
706
843
|
throw new Error("use-global ch\u1ECDn nh\u01B0ng kh\xF4ng \u0111\u1ECDc \u0111\u01B0\u1EE3c global settings.");
|
|
@@ -725,14 +862,15 @@ async function runAiSetupPhase(args) {
|
|
|
725
862
|
|
|
726
863
|
// src/lib/test-ai-provider-by-detected-mode.ts
|
|
727
864
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
728
|
-
var
|
|
865
|
+
var FETCH_TIMEOUT_MS3 = 1e4;
|
|
729
866
|
var CLAUDE_PRINT_TIMEOUT_MS = 3e4;
|
|
730
867
|
var TEST_CHAT_MAX_TOKENS = 5;
|
|
731
868
|
var TEST_CHAT_PROMPT = "say ok";
|
|
869
|
+
var ANTHROPIC_API_VERSION2 = "2023-06-01";
|
|
732
870
|
async function testLLMLiteProvider(baseUrl, token, model) {
|
|
733
871
|
log.info(`Testing LLMLite provider: ${baseUrl} (key: ${maskApiKey(token)})`);
|
|
734
872
|
const controller = new AbortController();
|
|
735
|
-
const timer = setTimeout(() => controller.abort(),
|
|
873
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS3);
|
|
736
874
|
try {
|
|
737
875
|
const modelsRes = await fetch(`${baseUrl}/v1/models`, {
|
|
738
876
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -777,7 +915,7 @@ async function testLLMLiteProvider(baseUrl, token, model) {
|
|
|
777
915
|
log.dim(` Tokens used: ${tokens}`);
|
|
778
916
|
} catch (err) {
|
|
779
917
|
if (err.name === "AbortError") {
|
|
780
|
-
throw new Error(`Timeout ${
|
|
918
|
+
throw new Error(`Timeout ${FETCH_TIMEOUT_MS3 / 1e3}s. Check m\u1EA1ng / endpoint ${baseUrl}.`);
|
|
781
919
|
}
|
|
782
920
|
throw err;
|
|
783
921
|
} finally {
|
|
@@ -806,11 +944,63 @@ function testSubscriptionProvider() {
|
|
|
806
944
|
}
|
|
807
945
|
log.success(`Response: "${(result.stdout || "").trim().slice(0, 100)}"`);
|
|
808
946
|
}
|
|
947
|
+
async function testAnthropicProvider(baseUrl, apiKey, model) {
|
|
948
|
+
log.info(`Testing Anthropic Direct provider: ${baseUrl} (key: ${maskApiKey(apiKey)})`);
|
|
949
|
+
const controller = new AbortController();
|
|
950
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS3);
|
|
951
|
+
try {
|
|
952
|
+
const modelsRes = await fetch(`${baseUrl}/v1/models`, {
|
|
953
|
+
headers: {
|
|
954
|
+
"x-api-key": apiKey,
|
|
955
|
+
"anthropic-version": ANTHROPIC_API_VERSION2
|
|
956
|
+
},
|
|
957
|
+
signal: controller.signal
|
|
958
|
+
});
|
|
959
|
+
if (modelsRes.status === 401 || modelsRes.status === 403) {
|
|
960
|
+
throw new Error(`API key invalid (HTTP ${modelsRes.status}). Re-run: avatar ai setup`);
|
|
961
|
+
}
|
|
962
|
+
if (!modelsRes.ok) {
|
|
963
|
+
throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${modelsRes.status}).`);
|
|
964
|
+
}
|
|
965
|
+
const modelsJson = await modelsRes.json();
|
|
966
|
+
const models = (modelsJson.data || []).map((m) => typeof m.id === "string" ? m.id : null).filter((id) => id !== null);
|
|
967
|
+
log.success(`Connectivity OK \xB7 ${models.length} models available`);
|
|
968
|
+
log.info(`Testing message v\u1EDBi model "${model}"...`);
|
|
969
|
+
const msgRes = await fetch(`${baseUrl}/v1/messages`, {
|
|
970
|
+
method: "POST",
|
|
971
|
+
headers: {
|
|
972
|
+
"x-api-key": apiKey,
|
|
973
|
+
"anthropic-version": ANTHROPIC_API_VERSION2,
|
|
974
|
+
"Content-Type": "application/json"
|
|
975
|
+
},
|
|
976
|
+
body: JSON.stringify({
|
|
977
|
+
model,
|
|
978
|
+
max_tokens: TEST_CHAT_MAX_TOKENS,
|
|
979
|
+
messages: [{ role: "user", content: TEST_CHAT_PROMPT }]
|
|
980
|
+
}),
|
|
981
|
+
signal: controller.signal
|
|
982
|
+
});
|
|
983
|
+
if (!msgRes.ok) {
|
|
984
|
+
const errText = (await msgRes.text()).slice(0, 200);
|
|
985
|
+
throw new Error(`Message endpoint fail (HTTP ${msgRes.status}): ${errText}`);
|
|
986
|
+
}
|
|
987
|
+
const msgJson = await msgRes.json();
|
|
988
|
+
const text = (msgJson.content || []).map((c) => typeof c.text === "string" ? c.text : "").join("").trim().slice(0, 100);
|
|
989
|
+
log.success(`Response: "${text}"`);
|
|
990
|
+
} finally {
|
|
991
|
+
clearTimeout(timer);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
809
994
|
async function testAiProviderByDetectedMode(settings) {
|
|
810
995
|
const env = settings.env || {};
|
|
811
996
|
const baseUrl = typeof env.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL : void 0;
|
|
812
997
|
const token = typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN : void 0;
|
|
998
|
+
const apiKey = typeof env.ANTHROPIC_API_KEY === "string" ? env.ANTHROPIC_API_KEY : void 0;
|
|
813
999
|
const model = typeof settings.model === "string" ? settings.model : "default";
|
|
1000
|
+
if (apiKey && baseUrl) {
|
|
1001
|
+
await testAnthropicProvider(baseUrl, apiKey, model);
|
|
1002
|
+
return { ok: true, provider: "anthropic", message: "Anthropic Direct provider working" };
|
|
1003
|
+
}
|
|
814
1004
|
if (baseUrl && token) {
|
|
815
1005
|
await testLLMLiteProvider(baseUrl, token, model);
|
|
816
1006
|
return { ok: true, provider: "llmlite", message: "LLMLite provider working" };
|
|
@@ -856,12 +1046,27 @@ async function runAiStatus() {
|
|
|
856
1046
|
const env = settings.env || {};
|
|
857
1047
|
const baseUrl = typeof env.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL : void 0;
|
|
858
1048
|
const token = typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN : void 0;
|
|
1049
|
+
const apiKey = typeof env.ANTHROPIC_API_KEY === "string" ? env.ANTHROPIC_API_KEY : void 0;
|
|
859
1050
|
const model = typeof settings.model === "string" ? settings.model : void 0;
|
|
860
|
-
|
|
1051
|
+
let provider;
|
|
1052
|
+
let credentialDisplay;
|
|
1053
|
+
if (apiKey) {
|
|
1054
|
+
provider = baseUrl?.includes("api.anthropic.com") || apiKey.startsWith("sk-ant-") ? "Anthropic Direct" : "Custom (API key)";
|
|
1055
|
+
credentialDisplay = maskApiKey(apiKey);
|
|
1056
|
+
} else if (baseUrl && token) {
|
|
1057
|
+
provider = "LLMLite";
|
|
1058
|
+
credentialDisplay = maskApiKey(token);
|
|
1059
|
+
} else if (token) {
|
|
1060
|
+
provider = "Custom";
|
|
1061
|
+
credentialDisplay = maskApiKey(token);
|
|
1062
|
+
} else {
|
|
1063
|
+
provider = "Subscription (default)";
|
|
1064
|
+
credentialDisplay = "(kh\xF4ng set \u2014 d\xF9ng subscription auth)";
|
|
1065
|
+
}
|
|
861
1066
|
log.info(`Project: ${workspacePath}`);
|
|
862
1067
|
log.info(`Provider: ${provider}${baseUrl ? ` (${baseUrl})` : ""}`);
|
|
863
1068
|
log.info(`Model: ${model ?? "(default \u2014 Claude Code ch\u1ECDn)"}`);
|
|
864
|
-
log.info(`Token: ${
|
|
1069
|
+
log.info(`Token: ${credentialDisplay}`);
|
|
865
1070
|
}
|
|
866
1071
|
async function runAiTest() {
|
|
867
1072
|
const workspacePath = await ensureWorkspaceCwd();
|
|
@@ -891,7 +1096,12 @@ async function runAiReset(opts) {
|
|
|
891
1096
|
const { env: existingEnv, ...rest } = settings;
|
|
892
1097
|
const reset = { ...rest };
|
|
893
1098
|
if (existingEnv) {
|
|
894
|
-
const {
|
|
1099
|
+
const {
|
|
1100
|
+
ANTHROPIC_BASE_URL: _b,
|
|
1101
|
+
ANTHROPIC_AUTH_TOKEN: _t,
|
|
1102
|
+
ANTHROPIC_API_KEY: _k,
|
|
1103
|
+
...envRest
|
|
1104
|
+
} = existingEnv;
|
|
895
1105
|
if (Object.keys(envRest).length > 0) {
|
|
896
1106
|
reset.env = envRest;
|
|
897
1107
|
}
|
|
@@ -1549,7 +1759,7 @@ function installGitnexusViaNpm() {
|
|
|
1549
1759
|
}
|
|
1550
1760
|
|
|
1551
1761
|
// src/lib/prompt-recovery-action-on-failure.ts
|
|
1552
|
-
import { input as input3, select as
|
|
1762
|
+
import { input as input3, select as select4 } from "@inquirer/prompts";
|
|
1553
1763
|
var UserAbortedRecoveryError = class extends Error {
|
|
1554
1764
|
constructor(message) {
|
|
1555
1765
|
super(message);
|
|
@@ -1566,7 +1776,7 @@ async function promptRetryOrSkip(args) {
|
|
|
1566
1776
|
choices.push({ name: "B\u1ECF qua b\u01B0\u1EDBc n\xE0y v\xE0 ti\u1EBFp t\u1EE5c (Skip)", value: "skip" });
|
|
1567
1777
|
}
|
|
1568
1778
|
choices.push({ name: "T\u1EA1m ng\u01B0ng init \u2014 ch\u1EA1y l\u1EA1i sau (Abort)", value: "abort" });
|
|
1569
|
-
return await
|
|
1779
|
+
return await select4({
|
|
1570
1780
|
message: "C\xE1ch x\u1EED l\xFD?",
|
|
1571
1781
|
choices
|
|
1572
1782
|
});
|
|
@@ -1960,19 +2170,19 @@ function registerGitnexusCommand(program2) {
|
|
|
1960
2170
|
|
|
1961
2171
|
// src/commands/init.ts
|
|
1962
2172
|
import { basename, join as join22, relative as relative2, resolve } from "path";
|
|
1963
|
-
import { confirm as confirm5, input as input5, select as
|
|
2173
|
+
import { confirm as confirm5, input as input5, select as select9 } from "@inquirer/prompts";
|
|
1964
2174
|
import boxen5 from "boxen";
|
|
1965
2175
|
|
|
1966
2176
|
// src/lib/add-team-pack-submodule-with-retry-on-network-fail.ts
|
|
1967
2177
|
import { spawnSync as spawnSync13 } from "child_process";
|
|
1968
|
-
import { select as
|
|
2178
|
+
import { select as select6 } from "@inquirer/prompts";
|
|
1969
2179
|
|
|
1970
2180
|
// src/lib/team-pack-submodule-manager.ts
|
|
1971
2181
|
import { join as join16 } from "path";
|
|
1972
2182
|
|
|
1973
2183
|
// src/lib/check-team-pack-access-with-retry-loop.ts
|
|
1974
2184
|
import { spawnSync as spawnSync12 } from "child_process";
|
|
1975
|
-
import { confirm as confirm4, select as
|
|
2185
|
+
import { confirm as confirm4, select as select5 } from "@inquirer/prompts";
|
|
1976
2186
|
import boxen3 from "boxen";
|
|
1977
2187
|
function parseRepoSlugFromGitUrl(url) {
|
|
1978
2188
|
const httpsMatch = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
@@ -2051,7 +2261,7 @@ async function ensureTeamPackAccessWithRetry(args) {
|
|
|
2051
2261
|
while (true) {
|
|
2052
2262
|
const ghUser = getCurrentGhUser();
|
|
2053
2263
|
const ghUserDisplay = ghUser ?? "(ch\u01B0a gh auth)";
|
|
2054
|
-
const action = await
|
|
2264
|
+
const action = await select5({
|
|
2055
2265
|
message: "C\xE1ch x\u1EED l\xFD?",
|
|
2056
2266
|
choices: [
|
|
2057
2267
|
{
|
|
@@ -2168,7 +2378,7 @@ function openGithubSshKeysPage() {
|
|
|
2168
2378
|
}
|
|
2169
2379
|
}
|
|
2170
2380
|
async function handleSshPermissionError() {
|
|
2171
|
-
return await
|
|
2381
|
+
return await select6({
|
|
2172
2382
|
message: "SSH permission denied. C\xE1ch x\u1EED l\xFD?",
|
|
2173
2383
|
choices: [
|
|
2174
2384
|
{
|
|
@@ -2431,7 +2641,7 @@ function detectPackageManager() {
|
|
|
2431
2641
|
|
|
2432
2642
|
// src/lib/handle-remote-access-failure-with-account-switch.ts
|
|
2433
2643
|
import { spawnSync as spawnSync19 } from "child_process";
|
|
2434
|
-
import { input as input4, select as
|
|
2644
|
+
import { input as input4, select as select7 } from "@inquirer/prompts";
|
|
2435
2645
|
|
|
2436
2646
|
// src/lib/verify-git-remote-accessible.ts
|
|
2437
2647
|
import { spawnSync as spawnSync18 } from "child_process";
|
|
@@ -2515,7 +2725,7 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
|
|
|
2515
2725
|
log.dim(` L\xFD do: ${reason}${detail ? ` \u2014 ${detail.slice(0, 150)}` : ""}`);
|
|
2516
2726
|
log.info(getReasonHint(reason, currentUrl, ghUser));
|
|
2517
2727
|
if (ghUser) log.dim(` gh CLI hi\u1EC7n \u0111ang login: ${ghUser}`);
|
|
2518
|
-
const action = await
|
|
2728
|
+
const action = await select7({
|
|
2519
2729
|
message: "C\xE1ch x\u1EED l\xFD?",
|
|
2520
2730
|
choices: [
|
|
2521
2731
|
{
|
|
@@ -2824,7 +3034,7 @@ function linkExistingRemoteToWorkspace(args) {
|
|
|
2824
3034
|
|
|
2825
3035
|
// src/lib/safe-bootstrap-for-dirty-folder.ts
|
|
2826
3036
|
import { readdirSync } from "fs";
|
|
2827
|
-
import { select as
|
|
3037
|
+
import { select as select8 } from "@inquirer/prompts";
|
|
2828
3038
|
import { simpleGit as simpleGit3 } from "simple-git";
|
|
2829
3039
|
|
|
2830
3040
|
// src/lib/check-folder-has-git.ts
|
|
@@ -2961,7 +3171,7 @@ async function promptBootstrapStrategy(state, opts) {
|
|
|
2961
3171
|
if (opts.presetStrategy) return opts.presetStrategy;
|
|
2962
3172
|
if (opts.autoYes) return "stash";
|
|
2963
3173
|
if (state === "empty" || state === "clean") return "commit-all";
|
|
2964
|
-
return await
|
|
3174
|
+
return await select8({
|
|
2965
3175
|
message: state === "dirty" ? "Folder c\xF3 changes ch\u01B0a commit. C\xE1ch x\u1EED l\xFD:" : "Folder c\xF3 file ch\u01B0a version. C\xE1ch x\u1EED l\xFD:",
|
|
2966
3176
|
choices: [
|
|
2967
3177
|
{
|
|
@@ -3446,7 +3656,7 @@ async function runInit(opts) {
|
|
|
3446
3656
|
}
|
|
3447
3657
|
}
|
|
3448
3658
|
async function promptProjectStatus() {
|
|
3449
|
-
return await
|
|
3659
|
+
return await select9({
|
|
3450
3660
|
message: "T\xECnh tr\u1EA1ng d\u1EF1 \xE1n c\u1EE7a b\u1EA1n?",
|
|
3451
3661
|
choices: [
|
|
3452
3662
|
{ name: "1. \u0110\xE3 c\xF3 repo git remote (URL c\xF3 s\u1EB5n)", value: "existing-remote" },
|
|
@@ -3529,7 +3739,7 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
3529
3739
|
message: "T\xEAn d\u1EF1 \xE1n:",
|
|
3530
3740
|
validate: (v) => v.length > 0 ? true : "T\xEAn b\u1EAFt bu\u1ED9c"
|
|
3531
3741
|
});
|
|
3532
|
-
const visibility = opts.repoVisibility ?? await
|
|
3742
|
+
const visibility = opts.repoVisibility ?? await select9({
|
|
3533
3743
|
message: "Visibility?",
|
|
3534
3744
|
choices: [
|
|
3535
3745
|
{ name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
|
|
@@ -3604,7 +3814,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
|
|
|
3604
3814
|
return void 0;
|
|
3605
3815
|
}
|
|
3606
3816
|
await ensureGitHubReady();
|
|
3607
|
-
const visibility = opts.repoVisibility ?? await
|
|
3817
|
+
const visibility = opts.repoVisibility ?? await select9({
|
|
3608
3818
|
message: "Visibility?",
|
|
3609
3819
|
choices: [
|
|
3610
3820
|
{ name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
|
|
@@ -3730,7 +3940,7 @@ async function maybeCreateWorkspaceRemote(args) {
|
|
|
3730
3940
|
});
|
|
3731
3941
|
}
|
|
3732
3942
|
if (!shouldCreate) return;
|
|
3733
|
-
const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await
|
|
3943
|
+
const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await select9({
|
|
3734
3944
|
message: "Workspace visibility?",
|
|
3735
3945
|
choices: [
|
|
3736
3946
|
{ name: "private (m\u1EB7c \u0111\u1ECBnh, an to\xE0n)", value: "private" },
|
|
@@ -3749,7 +3959,7 @@ async function maybeCreateWorkspaceRemote(args) {
|
|
|
3749
3959
|
} catch (err) {
|
|
3750
3960
|
if (err instanceof CreateWorkspaceRemoteError && err.reason === "repo-exists") {
|
|
3751
3961
|
const fullName = err.fullName;
|
|
3752
|
-
const reuseAction = await
|
|
3962
|
+
const reuseAction = await select9({
|
|
3753
3963
|
message: `Repo '${fullName}' \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. C\xE1ch x\u1EED l\xFD?`,
|
|
3754
3964
|
choices: [
|
|
3755
3965
|
{
|
|
@@ -3818,7 +4028,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
|
|
|
3818
4028
|
}
|
|
3819
4029
|
choices.push({ name: "Nh\u1EADp t\xEAn workspace kh\xE1c (manual)", value: "manual" });
|
|
3820
4030
|
choices.push({ name: "T\u1EA1m ng\u01B0ng init", value: "abort" });
|
|
3821
|
-
const action = await
|
|
4031
|
+
const action = await select9({
|
|
3822
4032
|
message: "C\xE1ch x\u1EED l\xFD workspace path conflict?",
|
|
3823
4033
|
choices
|
|
3824
4034
|
});
|
|
@@ -4536,7 +4746,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
|
|
|
4536
4746
|
}
|
|
4537
4747
|
|
|
4538
4748
|
// src/commands/uninstall.ts
|
|
4539
|
-
var CLI_VERSION = "1.
|
|
4749
|
+
var CLI_VERSION = "1.6.0";
|
|
4540
4750
|
function registerUninstallCommand(program2) {
|
|
4541
4751
|
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) => {
|
|
4542
4752
|
try {
|
|
@@ -4618,7 +4828,7 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
4618
4828
|
}
|
|
4619
4829
|
|
|
4620
4830
|
// src/index.ts
|
|
4621
|
-
var CLI_VERSION2 = "1.
|
|
4831
|
+
var CLI_VERSION2 = "1.6.0";
|
|
4622
4832
|
var program = new Command();
|
|
4623
4833
|
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(
|
|
4624
4834
|
"beforeAll",
|