diff --git a/package.json b/package.json index 526ff1f..972b817 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agent-score/pay", - "version": "0.1.0-rc.17", + "version": "0.1.0-rc.18", "description": "CLI wallet for one-shell-command agent payments across x402 (Base) and MPP (Tempo, Solana)", "type": "module", "main": "./dist/index.js", diff --git a/src/banner.ts b/src/banner.ts new file mode 100644 index 0000000..78d51ac --- /dev/null +++ b/src/banner.ts @@ -0,0 +1,77 @@ +const BANNER_ART_COLORED = [ + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m█\x1b[38;2;23;83;96m█\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m█\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m█\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m█\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m█\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m█\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m█\x1b[38;2;65;126;110m█\x1b[38;2;66;128;111m█\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m█\x1b[38;2;72;134;113m█\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m█\x1b[38;2;79;142;116m█\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m█\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m█\x1b[38;2;99;163;123m█\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m█\x1b[38;2;106;170;125m█\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m█\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m█\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m \x1b[38;2;23;83;96m \x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m \x1b[38;2;30;90;98m \x1b[38;2;32;92;99m \x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m \x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m█\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m \x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m \x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m█\x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m \x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m \x1b[38;2;65;126;110m \x1b[38;2;66;128;111m \x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m \x1b[38;2;72;134;113m \x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m \x1b[38;2;79;142;116m \x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m \x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m \x1b[38;2;99;163;123m \x1b[38;2;100;164;123m█\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m \x1b[38;2;106;170;125m \x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m█\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m█\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m█\x1b[38;2;23;83;96m█\x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m \x1b[38;2;45;105;103m█\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m \x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m█\x1b[38;2;79;142;116m█\x1b[38;2;80;143;116m \x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m█\x1b[38;2;99;163;123m█\x1b[38;2;100;164;123m \x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m█\x1b[38;2;106;170;125m█\x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m \x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m \x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m \x1b[38;2;23;83;96m \x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m \x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m \x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m█\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m \x1b[38;2;79;142;116m \x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m \x1b[38;2;99;163;123m \x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m \x1b[38;2;106;170;125m \x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m█\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m \x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m█\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m█\x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m \x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m█\x1b[38;2;65;126;110m█\x1b[38;2;66;128;111m█\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m \x1b[38;2;70;133;113m█\x1b[38;2;72;134;113m█\x1b[38;2;73;136;114m \x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m█\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m \x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m \x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m \x1b[38;2;29;89;98m \x1b[38;2;30;90;98m \x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m \x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m \x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m \x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m \x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m \x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m \x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m \x1b[38;2;63;125;110m \x1b[38;2;65;126;110m \x1b[38;2;66;128;111m \x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m \x1b[38;2;72;134;113m \x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m \x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m \x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m \x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m \x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m \x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m \x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m \x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m \x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m\x1b[0m', +].join('\n'); + +const BANNER_ART_PLAIN = [ + '░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░', + '░░██░░░███░████░█░░█░███░░███░████░░██░░███░░████░░░░░███░░░██░░█░█', + '░█ █░█ ░█ ░██░█░ █ ░█ ░█ ░█ █░█ █░█ ░░░░░█ █░█ █░███', + '░████░█░██░███░░█ ██░░█░░ ██░░█░░░░█░░█░███ ░███░░░░░░███ ░████░ █', + '░█ █░█░ █░█ ░░█░ █░░█░░░ █░█░░░░█░░█░█ █░█ ░░░░░░█ ░░█ █░░█░', + '░█░░█░███ ░████░█░░█░░█░░███ ░████░ ██ ░█░░█░████░░░░░█░░░░█░░█░░█░', + '░ ░░ ░ ░░ ░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░ ░░░░░ ░░░░ ░░ ░░ ░', + '░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░', +].join('\n'); + +const TAGLINE = 'Universal agent-payment CLI'; + +const COMMANDS = [ + 'Pay any 402 / MPP endpoint:', + ' pay Send a paid HTTP request (multi-rail x402 + MPP)', + ' check Probe a URL for 402 and accepted rails (no money moves)', + ' balance USDC across base, solana, tempo', + ' discover Browse paid services in x402 Bazaar + MPP directory', + ' fund Show receive QR and poll for deposit', + '', + 'Agents (LLM tool-loop):', + ' --mcp Expose every command as MCP tools over stdio', + ' agent-guide Structured how-to-use for shell-tool agents', + ' skills add Sync skill files to Claude Code / Cursor / Amp', + '', + 'Account management:', + ' init First-run wallet setup (base + solana + tempo)', + ' passport Verify once with KYC; auto-attaches to gated merchants', + ' wallet Manage encrypted keystore (create / import / export / list)', + ' limits Persistent local spending caps', + ' history Past payments', + '', + 'Output formats:', + ' --json JSON envelope', + ' --format toon TOON envelope (default)', + ' --help Full command list and flags', +].join('\n'); + +const MIN_BANNER_COLS = 70; +const PLAIN_BRAND = 'AgentScore Pay'; + +function shouldUseColor(): boolean { + if (process.env.NO_COLOR) return false; + return true; +} + +export function renderBanner({ + cols, + color, +}: { + cols: number; + color: boolean; +}): string { + if (cols < MIN_BANNER_COLS) { + return `${PLAIN_BRAND}\n${TAGLINE}\n\n${COMMANDS}\n`; + } + const art = color ? BANNER_ART_COLORED : BANNER_ART_PLAIN; + return `${art}\n\n${TAGLINE}\n\n${COMMANDS}\n`; +} + +export function printBanner(): void { + if (!process.stderr.isTTY) return; + const cols = process.stderr.columns ?? 80; + process.stderr.write(renderBanner({ cols, color: shouldUseColor() })); +} diff --git a/src/index.ts b/src/index.ts index bbf6000..9c18dd5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import { printBanner } from './banner'; import { run } from './cli'; import { getNoticeIfNewer, refreshCacheAwaited, refreshCacheInBackground } from './update-check'; @@ -15,6 +16,12 @@ async function emitUpdateNoticeIfAvailable(): Promise { } } +if (process.argv.length === 2 && process.stderr.isTTY) { + printBanner(); + refreshCacheInBackground(); + process.exit(0); +} + run() .then(async () => { await emitUpdateNoticeIfAvailable(); diff --git a/tests/banner.test.ts b/tests/banner.test.ts new file mode 100644 index 0000000..28554f2 --- /dev/null +++ b/tests/banner.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, it } from 'vitest'; +import { renderBanner } from '../src/banner'; + +describe('banner', () => { + it('renders block-art logo when terminal is wide enough', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('░'); + expect(out).toContain('█'); + expect(out).toContain('Universal agent-payment CLI'); + }); + + it('falls back to plain-text brand on narrow terminals', () => { + const out = renderBanner({ cols: 40, color: false }); + expect(out).not.toContain('░'); + expect(out).toContain('AgentScore Pay'); + expect(out).toContain('Universal agent-payment CLI'); + }); + + it('uses full brand prefix, never bare Pay or PAY.SH', () => { + const wide = renderBanner({ cols: 100, color: false }); + const narrow = renderBanner({ cols: 40, color: false }); + expect(wide).not.toContain('PAY.SH'); + expect(narrow).not.toContain('PAY.SH'); + expect(narrow).toContain('AgentScore Pay'); + }); + + it('omits ANSI escapes when color is disabled', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).not.toContain('\x1b['); + }); + + it('includes ANSI escapes when color is enabled', () => { + const out = renderBanner({ cols: 100, color: true }); + expect(out).toContain('\x1b['); + }); + + it('plain-text fallback never includes ANSI escapes regardless of color flag', () => { + const out = renderBanner({ cols: 40, color: true }); + expect(out).not.toContain('\x1b['); + }); + + it('plain art width fits within 70 cols (the threshold for falling back)', () => { + const out = renderBanner({ cols: 100, color: false }); + const artLines = out.split('\n').filter((line) => line.includes('░') || line.includes('█')); + for (const line of artLines) { + expect(line.length).toBeLessThanOrEqual(70); + } + }); + + it('groups commands by audience role', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('Pay any 402 / MPP endpoint:'); + expect(out).toContain('Agents (LLM tool-loop):'); + expect(out).toContain('Account management:'); + expect(out).toContain('Output formats:'); + }); + + it('lists real key-free commands without the binary prefix', () => { + const out = renderBanner({ cols: 100, color: false }); + for (const cmd of [ + 'pay', + 'check', + 'balance', + 'discover', + 'fund', + 'passport', + 'agent-guide', + 'skills add', + 'init', + 'wallet', + 'limits', + 'history', + ]) { + expect(out).toContain(cmd); + } + // Lines starting with the binary prefix would be the old format; new format omits it. + const lines = out.split('\n'); + const prefixedLines = lines.filter((l) => l.startsWith(' agentscore-pay ')); + expect(prefixedLines).toHaveLength(0); + }); + + it('omits commands that require an API key', () => { + const out = renderBanner({ cols: 100, color: false }); + // These all route through getClient() and throw without AGENTSCORE_API_KEY. + // Bare invocation should only surface commands that work out-of-the-box. + for (const apiKeyOnly of ['reputation', 'assess', 'credentials', 'associate-wallet', 'sessions']) { + const lines = out.split('\n'); + const matches = lines.filter((l) => l.match(new RegExp(`^\\s+${apiKeyOnly}\\s`))); + expect(matches).toHaveLength(0); + } + }); + + it('surfaces dual-audience output flags', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('--json'); + expect(out).toContain('--format toon'); + expect(out).toContain('--help'); + expect(out).toContain('--mcp'); + }); + + it('does not reference the unbuilt `fund options` subcommand', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).not.toContain('fund options'); + }); +});